Compare commits

..

23 Commits

Author SHA1 Message Date
Quentin McGaw (desktop)
af358f777b Feat: pull filter ipv6 if OPENVPN_IPV6 is off 2021-07-19 01:46:20 +00:00
Quentin McGaw (desktop)
c0d27b4bfc Maint: rework openvpn restart on unhealthy 2021-07-18 03:17:48 +00:00
Quentin McGaw (desktop)
7e50c95823 Maint: minor DNS loop fixes and changes 2021-07-16 21:21:09 +00:00
Quentin McGaw (desktop)
39068dda17 Maint: rework Openvpn run loop 2021-07-16 21:20:34 +00:00
Quentin McGaw (desktop)
8185979ca4 Fix: deadlock on dns shutdown when starting up 2021-07-16 20:11:57 +00:00
Quentin McGaw (desktop)
7c44188130 Fix: controlled interrupt exit for subprograms
- Openvpn and Unbound do not receive OS signals
- Openvpn and Unbound run in a different process group than the entrypoint
- Openvpn and Unbound are gracefully shutdown by the entrypoint
- Update golibs with a modified command package
- Update dns to v1.9.0 where Unbound is luanched in its own group
2021-07-16 20:04:17 +00:00
Quentin McGaw (desktop)
c2d527bbd3 Fix: openvpn run loop panic about stdout streams 2021-07-16 19:02:04 +00:00
Quentin McGaw (desktop)
ac3ff095a1 Maint: rework DNS run loop
- Fix fragile user triggered logic
- Simplify state
- Lock loop when crashed
2021-07-16 19:00:56 +00:00
Quentin McGaw (desktop)
0ed738cd61 Maint: make all set status context aware 2021-07-16 00:49:59 +00:00
Quentin McGaw (desktop)
6bbb7c8f7d Maint: remove outdated Auth log warning about PIA 2021-07-16 00:49:50 +00:00
Quentin McGaw (desktop)
d29429808c Maint: deduplicate error logs for goshutdown 2021-07-15 23:02:33 +00:00
Quentin McGaw (desktop)
09eccd7cd9 Fix: events routing behavior when version information is disabled 2021-07-15 22:43:30 +00:00
Quentin McGaw (desktop)
bb2b8b4514 Fix: events routing exit when gluetun stops at start 2021-07-15 22:42:58 +00:00
Quentin McGaw (desktop)
e20b9c5774 Doc: simplify metdata and move it at top of readme 2021-07-14 22:17:51 +00:00
Quentin McGaw (desktop)
3badfa197a Doc: use native markdown for svg title image 2021-07-14 22:08:40 +00:00
Quentin McGaw (desktop)
dee372e71b Doc: add video 2021-07-14 00:31:27 +00:00
Quentin McGaw (desktop)
679be6e1bd Feat: clean suffix new lines for credentials 2021-07-06 14:37:59 +00:00
Quentin McGaw (desktop)
92212fdd11 Fix: Cert validation for IPVanish 2021-07-01 18:28:24 +00:00
Quentin McGaw (desktop)
a6fb1ad9ef Feat: update IPVanish server information 2021-07-01 18:28:12 +00:00
Quentin McGaw (desktop)
87d712fbd7 Feature: update ProtonVPN server information 2021-06-28 15:30:35 +00:00
Quentin McGaw (desktop)
023809f099 Feature: upgrade to Alpine 3.14
- Release note: https://wiki.alpinelinux.org/wiki/Release_Notes_for_Alpine_3.14.0
2021-06-25 19:01:00 +00:00
Quentin McGaw (desktop)
ace37370d1 Maint: xcputranslate version as build argument 2021-06-25 18:57:04 +00:00
Quentin McGaw (desktop)
8efbd4fac1 Maint: download golangci-lint from qmcgaw/binpot 2021-06-25 18:56:18 +00:00
52 changed files with 2194 additions and 1773 deletions

View File

@@ -1,15 +1,18 @@
ARG ALPINE_VERSION=3.13
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
FROM --platform=$BUILDPLATFORM qmcgaw/xcputranslate:v0.6.0 AS xcputranslate
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${ALPINE_VERSION} AS base
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${GO_ALPINE_VERSION} AS base
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
RUN apk --update add git g++
ENV CGO_ENABLED=0
ARG GOLANGCI_LINT_VERSION=v1.41.1
RUN go get github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}
COPY --from=golangci-lint /bin /go/bin/golangci-lint
WORKDIR /tmp/gobuild
COPY go.mod go.sum ./
RUN go mod download

View File

@@ -7,22 +7,36 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**:
<img height="250" src="https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg?sanitize=true">
![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg)
[![Size](https://img.shields.io/docker/image-size/qmcgaw/gluetun?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
[![Size](https://img.shields.io/docker/image-size/qmcgaw/gluetun/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags)
[![Build status](https://github.com/qdm12/gluetun/workflows/CI/badge.svg)](https://github.com/qdm12/gluetun/actions?query=workflow%3ACI)
[![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/private-internet-access)
[![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker pulls qmcgaw/gluetun](https://img.shields.io/docker/pulls/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker pulls qmcgaw/private-internet-access](https://img.shields.io/docker/pulls/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/gluetun](https://img.shields.io/docker/stars/qmcgaw/gluetun.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
[![Docker stars qmcgaw/private-internet-access](https://img.shields.io/docker/stars/qmcgaw/private-internet-access.svg)](https://hub.docker.com/r/qmcgaw/gluetun)
![Last release](https://img.shields.io/github/release/qdm12/gluetun?label=Last%20release)
![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/gluetun?sort=semver&label=Last%20Docker%20tag)
![GitHub Release Date](https://img.shields.io/github/release-date/qdm12/gluetun?label=Last%20release%20date)
[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/gluetun?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags?page=1&ordering=last_updated)
![GitHub last release date](https://img.shields.io/github/release-date/qdm12/gluetun?label=Last%20release%20date)
![Commits since release](https://img.shields.io/github/commits-since/qdm12/gluetun/latest?sort=semver)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits)
[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/gluetun/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/gluetun/tags)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits/master)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/graphs/contributors)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
[![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/gluetun)](https://github.com/qdm12/gluetun)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/gluetun)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/gluetun)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/gluetun)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=gluetun.readme)
## Quick links
@@ -35,6 +49,9 @@ 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:
[![Video Gif](https://i.imgur.com/CetWunc.gif)](https://youtu.be/0F6I03LQcI4)
## Features
@@ -101,25 +118,3 @@ The following points are all optional but should give you insights on all the po
## License
[![MIT](https://img.shields.io/github/license/qdm12/gluetun)](https://github.com/qdm12/gluetun/master/LICENSE)
## Metadata
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/commits)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/gluetun.svg)](https://github.com/qdm12/gluetun/issues?q=is%3Aissue+is%3Aclosed)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=gluetun.readme)
![GitHub stars](https://img.shields.io/github/stars/qdm12/gluetun?style=social)
![GitHub watchers](https://img.shields.io/github/watchers/qdm12/gluetun?style=social)
![Contributors](https://img.shields.io/github/contributors/qdm12/gluetun)
![GitHub forks](https://img.shields.io/github/forks/qdm12/gluetun?style=social)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/gluetun)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/gluetun)
[![dockeri.co](https://dockeri.co/image/qmcgaw/gluetun)](https://hub.docker.com/r/qmcgaw/gluetun)
![Docker Layers for latest](https://img.shields.io/microbadger/layers/qmcgaw/gluetun/latest?label=Docker%20image%20layers)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/gluetun)

View File

@@ -275,8 +275,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
}
} // TODO move inside firewall?
healthy := make(chan bool)
// Shutdown settings
const defaultShutdownTimeout = 400 * time.Millisecond
defaultShutdownOnSuccess := func(goRoutineName string) {
@@ -288,7 +286,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
defaultGoRoutineSettings := goshutdown.GoRoutineSettings{Timeout: defaultShutdownTimeout}
defaultGroupSettings := goshutdown.GroupSettings{
Timeout: defaultShutdownTimeout,
OnFailure: defaultShutdownOnFailure,
OnSuccess: defaultShutdownOnSuccess,
}
@@ -297,7 +294,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, tunnelReadyCh, healthy)
ovpnConf, firewallConf, routingConf, logger, httpClient, os.OpenFile, tunnelReadyCh)
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
// wait for restartOpenvpn
@@ -359,19 +356,20 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
controlServerLogging := allSettings.ControlServer.Log
httpServer := server.New(controlServerAddress, controlServerLogging,
logger.NewChild(logging.Settings{Prefix: "http server: "}),
buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
"http server", defaultGoRoutineSettings)
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
logger.NewChild(logging.Settings{Prefix: "http server: "}),
buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
go httpServer.Run(httpServerCtx, httpServerDone)
controlGroupHandler.Add(httpServerHandler)
healthcheckServer := healthcheck.NewServer(constants.HealthcheckAddress,
logger.NewChild(logging.Settings{Prefix: "healthcheck: "}))
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
healthcheckServer := healthcheck.NewServer(
constants.HealthcheckAddress, healthLogger, openvpnLooper)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
"HTTP health server", defaultGoRoutineSettings)
go healthcheckServer.Run(healthServerCtx, healthy, healthServerDone)
go healthcheckServer.Run(healthServerCtx, healthServerDone)
const orderShutdownTimeout = 3 * time.Second
orderSettings := goshutdown.OrderSettings{
@@ -385,7 +383,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
// Start openvpn for the first time in a blocking call
// until openvpn is launched
_, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable
_, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable
<-ctx.Done()
@@ -454,7 +452,7 @@ func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo model
}
if unboundLooper.GetSettings().Enabled {
_, _ = unboundLooper.SetStatus(constants.Running)
_, _ = unboundLooper.ApplyStatus(ctx, constants.Running)
}
restartTickerCancel() // stop previous restart tickers
@@ -463,12 +461,8 @@ func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo model
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
// Runs the Public IP getter job once
_, _ = publicIPLooper.SetStatus(constants.Running)
if !versionInformation {
break
}
if first {
_, _ = publicIPLooper.SetStatus(ctx, constants.Running)
if versionInformation && first {
first = false
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {

4
go.mod
View File

@@ -5,8 +5,8 @@ go 1.16
require (
github.com/fatih/color v1.12.0
github.com/golang/mock v1.6.0
github.com/qdm12/dns v1.8.0
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb
github.com/qdm12/dns v1.9.0
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80
github.com/qdm12/goshutdown v0.1.0
github.com/qdm12/ss-server v0.2.0
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e

7
go.sum
View File

@@ -63,10 +63,11 @@ github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMg
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.8.0 h1:GZ40kptmfDHOMNxBKWSA4zrbNyGm41BA57zv2MaDtCI=
github.com/qdm12/dns v1.8.0/go.mod h1:P2mm63NDYZdx2NAd5CVLM0FBnNdi1ZgVjsRSnX+96vg=
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb h1:5WkOssTWl6Tv2H7VFb2jwB08A7BxxNCebkkpvz1PzrY=
github.com/qdm12/dns v1.9.0 h1:p4g/BfbpQ+gJRpQdklDAnybkjds+OuenF0wEGoZ8/AI=
github.com/qdm12/dns v1.9.0/go.mod h1:fqZoDf3VzddnKBMNI/OzZUp5H4dO0VBw1fp4qPkolOg=
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80 h1:rvH2MSs8RXEfuXivzoYCim6tRNPzdqjBzqJq8w4Tc0k=
github.com/qdm12/golibs v0.0.0-20210716185557-66793f4ddd80/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
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/ss-server v0.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g=

View File

@@ -18,6 +18,12 @@ var (
ErrFilesDoNotExist = errors.New("files do not exist")
)
func cleanSuffix(value string) string {
value = strings.TrimSuffix(value, "\n")
value = strings.TrimSuffix(value, "\r")
return value
}
func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKeys []string) (value string, err error) {
envOptions := []params.OptionSetter{
params.Compulsory(), // to fallback on file reading
@@ -27,6 +33,7 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
}
value, envErr := r.env.Get(envKey, envOptions...)
if envErr == nil {
value = cleanSuffix(value)
return value, nil
}
@@ -55,7 +62,7 @@ func (r *reader) getFromEnvOrSecretFile(envKey string, compulsory bool, retroKey
}
value = string(b)
value = strings.TrimSuffix(value, "\n")
value = cleanSuffix(value)
if compulsory && len(value) == 0 {
return "", ErrSecretFileIsEmpty
}

View File

@@ -45,7 +45,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Albania", City: "Tirana", Hostname: "tia-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 28, 3}}},
{Country: "Albania", City: "Tirana", Hostname: "tia-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 28, 5}}},
{Country: "Albania", City: "Tirana", Hostname: "tia-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 28, 7}}},
{Country: "Albania", City: "Tirana", Hostname: "tia-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 28, 9}}},
{Country: "Argentina", City: "Buenos Aires", Hostname: "eze-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 69, 10}}},
{Country: "Argentina", City: "Buenos Aires", Hostname: "eze-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 69, 16}}},
{Country: "Argentina", City: "Buenos Aires", Hostname: "eze-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 69, 22}}},
@@ -63,7 +62,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Australia", City: "Melbourne", Hostname: "mel-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 17}}},
{Country: "Australia", City: "Melbourne", Hostname: "mel-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 11}}},
{Country: "Australia", City: "Melbourne", Hostname: "mel-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 19}}},
{Country: "Australia", City: "Melbourne", Hostname: "mel-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 13}}},
{Country: "Australia", City: "Melbourne", Hostname: "mel-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 21}}},
{Country: "Australia", City: "Melbourne", Hostname: "mel-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 209, 252, 15}}},
{Country: "Australia", City: "Perth", Hostname: "per-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 107, 197, 36}}},
@@ -102,19 +100,18 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Australia", City: "Sydney", Hostname: "syd-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 209, 164}}},
{Country: "Australia", City: "Sydney", Hostname: "syd-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 209, 170}}},
{Country: "Australia", City: "Sydney", Hostname: "syd-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 209, 176}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 2}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 8}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 14}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 20}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 26}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 32}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 140}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 150}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 160}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 170}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 180}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 190}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 44, 200}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 2}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 8}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 14}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 20}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 26}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 130}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 136}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 142}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 148}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 154}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 160}}},
{Country: "Austria", City: "Vienna", Hostname: "vie-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 110, 166}}},
{Country: "Belgium", City: "Brussels", Hostname: "bru-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{37, 120, 218, 99}}},
{Country: "Belgium", City: "Brussels", Hostname: "bru-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{37, 120, 218, 101}}},
{Country: "Belgium", City: "Brussels", Hostname: "bru-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{37, 120, 218, 103}}},
@@ -201,6 +198,11 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Canada", City: "Vancouver", Hostname: "yvr-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{107, 181, 189, 138}}},
{Country: "Canada", City: "Vancouver", Hostname: "yvr-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{107, 181, 189, 186}}},
{Country: "Canada", City: "Vancouver", Hostname: "yvr-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{107, 181, 189, 227}}},
{Country: "Chile", City: "Santiago", Hostname: "scl-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{191, 101, 28, 5}}},
{Country: "Chile", City: "Santiago", Hostname: "scl-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{191, 101, 28, 9}}},
{Country: "Chile", City: "Santiago", Hostname: "scl-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{191, 101, 28, 13}}},
{Country: "Chile", City: "Santiago", Hostname: "scl-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{191, 101, 28, 17}}},
{Country: "Chile", City: "Santiago", Hostname: "scl-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{191, 101, 28, 21}}},
{Country: "Colombia", City: "Bogota", Hostname: "bog-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 68, 11}}},
{Country: "Colombia", City: "Bogota", Hostname: "bog-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 68, 17}}},
{Country: "Colombia", City: "Bogota", Hostname: "bog-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{199, 33, 68, 23}}},
@@ -213,20 +215,23 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Croatia", City: "Zagreb", Hostname: "zag-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{85, 10, 57, 221}}},
{Country: "Croatia", City: "Zagreb", Hostname: "zag-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{85, 10, 57, 227}}},
{Country: "Croatia", City: "Zagreb", Hostname: "zag-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{85, 10, 57, 233}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 150}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 170}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 190}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 210}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 2}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 8}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 14}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 20}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 39}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 45}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 51}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 57}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 63}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{104, 238, 39, 69}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 39}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 45}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 51}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 57}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 63}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 130}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 136}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 142}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 148}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 154}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 160}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 3}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 9}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 15}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 21}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 27}}},
{Country: "Czech Republic", City: "Prague", Hostname: "prg-c26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 109, 33}}},
{Country: "Denmark", City: "Copenhagen", Hostname: "cph-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 245, 84, 8}}},
{Country: "Denmark", City: "Copenhagen", Hostname: "cph-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 245, 84, 10}}},
{Country: "Denmark", City: "Copenhagen", Hostname: "cph-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 245, 84, 12}}},
@@ -268,20 +273,20 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "France", City: "Marseille", Hostname: "mrs-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 82, 8}}},
{Country: "France", City: "Marseille", Hostname: "mrs-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 82, 14}}},
{Country: "France", City: "Marseille", Hostname: "mrs-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 82, 20}}},
{Country: "France", City: "Paris", Hostname: "par-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 13}}},
{Country: "France", City: "Paris", Hostname: "par-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 19}}},
{Country: "France", City: "Paris", Hostname: "par-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 25}}},
{Country: "France", City: "Paris", Hostname: "par-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 31}}},
{Country: "France", City: "Paris", Hostname: "par-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 37}}},
{Country: "France", City: "Paris", Hostname: "par-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 43}}},
{Country: "France", City: "Paris", Hostname: "par-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 49}}},
{Country: "France", City: "Paris", Hostname: "par-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 55}}},
{Country: "France", City: "Paris", Hostname: "par-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 61}}},
{Country: "France", City: "Paris", Hostname: "par-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 67}}},
{Country: "France", City: "Paris", Hostname: "par-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 73}}},
{Country: "France", City: "Paris", Hostname: "par-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 79}}},
{Country: "France", City: "Paris", Hostname: "par-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 85}}},
{Country: "France", City: "Paris", Hostname: "par-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 91}}},
{Country: "France", City: "Paris", Hostname: "par-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 17}}},
{Country: "France", City: "Paris", Hostname: "par-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 21}}},
{Country: "France", City: "Paris", Hostname: "par-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 25}}},
{Country: "France", City: "Paris", Hostname: "par-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 29}}},
{Country: "France", City: "Paris", Hostname: "par-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 33}}},
{Country: "France", City: "Paris", Hostname: "par-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 37}}},
{Country: "France", City: "Paris", Hostname: "par-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 41}}},
{Country: "France", City: "Paris", Hostname: "par-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 45}}},
{Country: "France", City: "Paris", Hostname: "par-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 49}}},
{Country: "France", City: "Paris", Hostname: "par-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 53}}},
{Country: "France", City: "Paris", Hostname: "par-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 57}}},
{Country: "France", City: "Paris", Hostname: "par-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 61}}},
{Country: "France", City: "Paris", Hostname: "par-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 65}}},
{Country: "France", City: "Paris", Hostname: "par-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 212, 69}}},
{Country: "France", City: "Paris", Hostname: "par-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 97}}},
{Country: "France", City: "Paris", Hostname: "par-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 103}}},
{Country: "France", City: "Paris", Hostname: "par-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 10, 232, 109}}},
@@ -338,22 +343,22 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Germany", City: "Frankfurt", Hostname: "fra-a42.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 93, 19}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-a43.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 93, 25}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-a44.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 93, 31}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 2}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 4}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 6}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 8}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 10}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 12}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 38}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 42}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 44}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 46}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 48}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 74}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 76}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 78}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 80}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 183, 94, 82}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 2}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 8}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 14}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 20}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 26}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 32}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 38}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 44}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 130}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 136}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 142}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 148}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 154}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 160}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 166}}},
{Country: "Germany", City: "Frankfurt", Hostname: "fra-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 111, 172}}},
{Country: "Greece", City: "Athens", Hostname: "ath-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{194, 150, 167, 98}}},
{Country: "Greece", City: "Athens", Hostname: "ath-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{194, 150, 167, 100}}},
{Country: "Greece", City: "Athens", Hostname: "ath-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{194, 150, 167, 102}}},
@@ -393,18 +398,32 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Israel", City: "Tel Aviv", Hostname: "tlv-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{87, 239, 255, 73}}},
{Country: "Israel", City: "Tel Aviv", Hostname: "tlv-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{87, 239, 255, 75}}},
{Country: "Israel", City: "Tel Aviv", Hostname: "tlv-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{87, 239, 255, 77}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 24}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 26}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 4}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 6}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 44}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 46}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 48}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 50}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 76}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 78}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 80}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 137, 82}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 4}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 10}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 16}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 22}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 28}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 34}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 40}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 46}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 52}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 58}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 64}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 70}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 76}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 82}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 88}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 94}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 100}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 106}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 112}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 118}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 124}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 130}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 136}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 142}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 148}}},
{Country: "Italy", City: "Milan", Hostname: "lin-a38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{69, 16, 157, 154}}},
{Country: "Japan", City: "Tokyo", Hostname: "nrt-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 217, 3}}},
{Country: "Japan", City: "Tokyo", Hostname: "nrt-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 217, 7}}},
{Country: "Japan", City: "Tokyo", Hostname: "nrt-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 217, 11}}},
@@ -435,7 +454,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Latvia", City: "Riga", Hostname: "rix-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 31, 23}}},
{Country: "Latvia", City: "Riga", Hostname: "rix-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{80, 246, 31, 25}}},
{Country: "Luxembourg", City: "Luxembourg", Hostname: "lux-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 151, 3}}},
{Country: "Luxembourg", City: "Luxembourg", Hostname: "lux-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 151, 5}}},
{Country: "Malaysia", City: "Kuala Lumpur", Hostname: "kul-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{202, 73, 14, 57}}},
{Country: "Malaysia", City: "Kuala Lumpur", Hostname: "kul-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{202, 73, 14, 53}}},
{Country: "Malaysia", City: "Kuala Lumpur", Hostname: "kul-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 246, 112, 197}}},
@@ -448,56 +466,52 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Mexico", City: "Guadalajara", Hostname: "gdl-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{143, 255, 58, 53}}},
{Country: "Moldova", City: "Chisinau", Hostname: "kiv-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 163, 46, 147}}},
{Country: "Moldova", City: "Chisinau", Hostname: "kiv-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 163, 46, 153}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 24}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 30}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 36}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 42}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 48}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 54}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 60}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 66}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 72}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 78}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 84}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 90}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 96}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 102}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 108}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 114}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 120}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 126}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 132}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 138}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 144}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 150}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 156}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 162}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 168}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 174}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 180}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 186}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 192}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 198}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 204}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 210}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 216}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 222}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 228}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 234}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 240}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 3}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 9}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 15}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 21}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 27}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 33}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 39}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 45}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 51}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 57}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 63}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 69}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 75}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 81}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 87}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 91}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 97}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 103}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 109}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 115}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 121}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 127}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 133}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 139}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 145}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 151}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 157}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 163}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 170}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 176}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 182}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 188}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 194}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 200}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 206}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 212}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 218}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 224}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 88, 230}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 12, 246}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 7}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a50.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 61}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 67}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 73}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 85}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a55.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 91}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a56.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 97}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a57.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 103}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a58.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 109}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a59.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 115}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a60.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 121}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-a61.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{2, 58, 13, 127}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-c37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{172, 83, 45, 131}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-c38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{172, 83, 45, 137}}},
{Country: "Netherlands", City: "Amsterdam", Hostname: "ams-c39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{172, 83, 45, 143}}},
@@ -590,7 +604,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Slovakia", City: "Bratislava", Hostname: "bts-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 87, 28}}},
{Country: "Slovakia", City: "Bratislava", Hostname: "bts-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 87, 38}}},
{Country: "Slovakia", City: "Bratislava", Hostname: "bts-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 87, 48}}},
{Country: "Slovenia", City: "Ljubljana", Hostname: "lju-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{195, 158, 249, 68}}},
{Country: "Slovenia", City: "Ljubljana", Hostname: "lju-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{195, 158, 249, 70}}},
{Country: "Slovenia", City: "Ljubljana", Hostname: "lju-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{195, 158, 249, 72}}},
{Country: "Slovenia", City: "Ljubljana", Hostname: "lju-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{195, 158, 249, 74}}},
@@ -632,6 +645,10 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Spain", City: "Madrid", Hostname: "mad-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 214, 182}}},
{Country: "Spain", City: "Madrid", Hostname: "mad-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 214, 188}}},
{Country: "Spain", City: "Madrid", Hostname: "mad-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 214, 194}}},
{Country: "Spain", City: "Valencia", Hostname: "vlc-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 150, 3}}},
{Country: "Spain", City: "Valencia", Hostname: "vlc-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 150, 5}}},
{Country: "Spain", City: "Valencia", Hostname: "vlc-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 150, 7}}},
{Country: "Spain", City: "Valencia", Hostname: "vlc-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 153, 150, 9}}},
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 18}}},
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 24}}},
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 30}}},
@@ -662,18 +679,18 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 180}}},
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 186}}},
{Country: "Sweden", City: "Stockholm", Hostname: "sto-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 147, 213, 192}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 5}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 7}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 9}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 11}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 15}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 121}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 141}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 151}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 161}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 171}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 181}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 82, 223, 191}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 2}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 8}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 14}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 20}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 26}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 32}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 130}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 136}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 142}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 148}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 154}}},
{Country: "Switzerland", City: "Zurich", Hostname: "zrh-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 108, 160}}},
{Country: "Taiwan", City: "Taipei", Hostname: "tpe-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 4, 31, 133}}},
{Country: "Taiwan", City: "Taipei", Hostname: "tpe-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{103, 4, 31, 135}}},
{Country: "Ukraine", City: "Kiev", Hostname: "iev-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{139, 28, 38, 28}}},
@@ -701,65 +718,68 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 164}}},
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 170}}},
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 176}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 9}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 15}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 21}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 27}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 33}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 39}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 45}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 51}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 57}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 63}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 69}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 75}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 81}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 87}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 93}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 99}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 105}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 111}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 117}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 123}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 129}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 135}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 141}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 147}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 153}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 159}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 165}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 171}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 177}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 183}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 189}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 195}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 201}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 207}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 213}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 219}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 225}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 231}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 237}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 220, 243}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 4}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a42.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 10}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a43.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 16}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a44.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 22}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a45.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 28}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a46.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 34}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a47.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 40}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a48.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 46}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a49.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 52}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a50.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 58}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 64}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 70}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a53.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 76}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 82}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a55.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 88}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a56.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 94}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a57.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 100}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a58.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 106}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a59.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{5, 180, 221, 112}}},
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 196}}},
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 202}}},
{Country: "United Kingdom", City: "Glasgow", Hostname: "gla-c06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 108, 105, 208}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 86}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 92}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 98}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 104}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 110}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 116}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 122}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 128}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 134}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 140}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 2}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 8}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 14}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 20}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 26}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 32}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 38}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 44}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 50}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 56}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 62}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 68}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 74}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 80}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 146}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 152}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 158}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 164}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 170}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 176}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 182}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 188}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 194}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 200}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 206}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 212}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 218}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 224}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 230}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 236}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 242}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a42.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 248}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a43.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 116, 254}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a44.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 6}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a45.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 12}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a46.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 18}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a47.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 24}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a48.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 30}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a49.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 36}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a50.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 42}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 48}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 54}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a53.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 60}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 66}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a55.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 72}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a56.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 78}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a57.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 84}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a58.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 90}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a59.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 117, 96}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a60.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 85, 10}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a61.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 85, 12}}},
{Country: "United Kingdom", City: "London", Hostname: "lon-a62.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{176, 67, 85, 14}}},
@@ -783,15 +803,15 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c07.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 242, 7, 10}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{89, 238, 142, 242}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{89, 238, 142, 244}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 5}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 7}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 9}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 11}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 13}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 15}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 17}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 19}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{185, 244, 9, 21}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 50}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 44}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 38}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 32}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 26}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 20}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 14}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 8}}},
{Country: "United Kingdom", City: "Manchester", Hostname: "man-c18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 121, 2}}},
{Country: "United States", City: "Ashburn", Hostname: "iad-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 203, 2}}},
{Country: "United States", City: "Ashburn", Hostname: "iad-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 203, 8}}},
{Country: "United States", City: "Ashburn", Hostname: "iad-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 245, 203, 14}}},
@@ -898,6 +918,7 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Ashburn", Hostname: "iad-b26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 73, 69}}},
{Country: "United States", City: "Ashburn", Hostname: "iad-b27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 73, 75}}},
{Country: "United States", City: "Ashburn", Hostname: "iad-b28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 73, 81}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{64, 145, 67, 7}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{64, 145, 67, 21}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{64, 145, 67, 30}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{64, 145, 67, 36}}},
@@ -919,6 +940,7 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Atlanta", Hostname: "atl-a20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 32}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 38}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 44}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 128}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 134}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 140}}},
{Country: "United States", City: "Atlanta", Hostname: "atl-a26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{209, 107, 196, 146}}},
@@ -1069,7 +1091,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Boston", Hostname: "bos-c49.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 169}}},
{Country: "United States", City: "Boston", Hostname: "bos-c50.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 171}}},
{Country: "United States", City: "Boston", Hostname: "bos-c51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 209}}},
{Country: "United States", City: "Boston", Hostname: "bos-c52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 211}}},
{Country: "United States", City: "Boston", Hostname: "bos-c54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 215}}},
{Country: "United States", City: "Boston", Hostname: "bos-c55.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 217}}},
{Country: "United States", City: "Boston", Hostname: "bos-c56.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{198, 181, 163, 219}}},
@@ -1086,8 +1107,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Boston", Hostname: "bos-c68.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 171, 20}}},
{Country: "United States", City: "Boston", Hostname: "bos-c69.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 171, 22}}},
{Country: "United States", City: "Boston", Hostname: "bos-c70.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 171, 24}}},
{Country: "United States", City: "Boston", Hostname: "bos-c71.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 171, 26}}},
{Country: "United States", City: "Boston", Hostname: "bos-c72.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{45, 56, 171, 28}}},
{Country: "United States", City: "Charlotte", Hostname: "clt-c01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 213, 88, 2}}},
{Country: "United States", City: "Charlotte", Hostname: "clt-c02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 213, 88, 4}}},
{Country: "United States", City: "Charlotte", Hostname: "clt-c03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 213, 88, 6}}},
@@ -1214,31 +1233,26 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Chicago", Hostname: "chi-b39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 77, 68}}},
{Country: "United States", City: "Chicago", Hostname: "chi-b40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 77, 74}}},
{Country: "United States", City: "Chicago", Hostname: "chi-b41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 77, 80}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 2}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 4}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 6}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 8}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 26}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c22.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 28}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c23.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 30}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c24.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 44}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c25.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 46}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c26.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 48}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c27.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 50}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c28.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 52}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c29.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 54}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c30.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 80}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c31.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 82}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c32.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 84}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c33.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 86}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c34.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 88}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c35.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 90}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c36.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 116}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c37.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 118}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c38.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 120}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 122}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 124}}},
{Country: "United States", City: "Chicago", Hostname: "chi-c41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 193, 126}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 7}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 13}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 19}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 25}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b05.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 31}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b06.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 37}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b08.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 49}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b09.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 55}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b10.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 61}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b11.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 67}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b12.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 73}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b13.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 79}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b14.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 85}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b15.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 91}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b16.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 97}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b17.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 103}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b18.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 109}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b19.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 115}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b20.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 121}}},
{Country: "United States", City: "Cincinnati", Hostname: "cvg-b21.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 84, 127}}},
{Country: "United States", City: "Dallas", Hostname: "dal-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 214, 48}}},
{Country: "United States", City: "Dallas", Hostname: "dal-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 214, 52}}},
{Country: "United States", City: "Dallas", Hostname: "dal-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 214, 5}}},
@@ -1592,7 +1606,6 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "Miami", Hostname: "mia-a51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 255, 191, 2}}},
{Country: "United States", City: "Miami", Hostname: "mia-a52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 255, 191, 4}}},
{Country: "United States", City: "Miami", Hostname: "mia-a53.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 255, 191, 6}}},
{Country: "United States", City: "Miami", Hostname: "mia-a54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{173, 255, 191, 20}}},
{Country: "United States", City: "Miami", Hostname: "mia-b01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 87, 3}}},
{Country: "United States", City: "Miami", Hostname: "mia-b02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 87, 9}}},
{Country: "United States", City: "Miami", Hostname: "mia-b03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 87, 15}}},
@@ -1861,6 +1874,20 @@ func IpvanishServers() []models.IpvanishServer {
{Country: "United States", City: "San Jose", Hostname: "sjc-a39.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 151, 183, 230}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a40.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 151, 183, 236}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a41.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 151, 183, 242}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a42.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 7}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a43.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 8}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a44.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 14}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a45.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 20}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a46.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 26}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a47.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 32}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a48.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 38}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a49.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 44}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a50.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 50}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a51.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 56}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a52.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 62}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a53.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 68}}},
{Country: "United States", City: "San Jose", Hostname: "sjc-a54.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{216, 131, 122, 74}}},
{Country: "United States", City: "Seattle", Hostname: "sea-a01.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 223, 2}}},
{Country: "United States", City: "Seattle", Hostname: "sea-a02.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 223, 8}}},
{Country: "United States", City: "Seattle", Hostname: "sea-a03.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 223, 14}}},
{Country: "United States", City: "Seattle", Hostname: "sea-a04.ipvanish.com", TCP: false, UDP: true, IPs: []net.IP{{205, 185, 223, 35}}},

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ func GetAllServers() (allServers models.AllServers) {
},
Ipvanish: models.IpvanishServers{
Version: 1,
Timestamp: 1622430497,
Timestamp: 1625164062,
Servers: IpvanishServers(),
},
Ivpn: models.IvpnServers{
@@ -53,7 +53,7 @@ func GetAllServers() (allServers models.AllServers) {
},
Protonvpn: models.ProtonvpnServers{
Version: 1,
Timestamp: 1621791438,
Timestamp: 1624894186,
Servers: ProtonvpnServers(),
},
Pia: models.PiaServers{

View File

@@ -175,7 +175,7 @@ func Test_timestamps(t *testing.T) {
"Ipvanish": {
servers: allServers.Ipvanish.Servers,
timestamp: allServers.Ipvanish.Timestamp,
digest: "c62dcf98",
digest: "7c60dc5d",
},
"Ivpn": {
servers: allServers.Ivpn.Servers,
@@ -210,7 +210,7 @@ func Test_timestamps(t *testing.T) {
"Protonvpn": {
servers: allServers.Protonvpn.Servers,
timestamp: allServers.Protonvpn.Timestamp,
digest: "e7180296",
digest: "3b86393e",
},
"Purevpn": {
servers: allServers.Purevpn.Servers,

View File

@@ -6,7 +6,6 @@ import (
"errors"
"net"
"net/http"
"sync"
"time"
"github.com/qdm12/dns/pkg/blacklist"
@@ -24,23 +23,25 @@ type Looper interface {
Run(ctx context.Context, done chan<- struct{})
RunRestartTicker(ctx context.Context, done chan<- struct{})
GetStatus() (status models.LoopStatus)
SetStatus(status models.LoopStatus) (outcome string, err error)
ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetSettings() (settings configuration.DNS)
SetSettings(settings configuration.DNS) (outcome string)
SetSettings(ctx context.Context, settings configuration.DNS) (
outcome string)
}
type looper struct {
state state
state *state
conf unbound.Configurator
blockBuilder blacklist.Builder
client *http.Client
logger logging.Logger
loopLock sync.Mutex
start chan struct{}
running chan models.LoopStatus
stop chan struct{}
stopped chan struct{}
updateTicker chan struct{}
userTrigger bool
start <-chan struct{}
running chan<- models.LoopStatus
stop <-chan struct{}
stopped chan<- struct{}
updateTicker <-chan struct{}
backoffTime time.Duration
timeNow func() time.Time
timeSince func(time.Time) time.Duration
@@ -51,20 +52,26 @@ const defaultBackoffTime = 10 * time.Second
func NewLooper(conf unbound.Configurator, settings configuration.DNS, client *http.Client,
logger logging.Logger, openFile os.OpenFileFunc) Looper {
start := make(chan struct{})
running := make(chan models.LoopStatus)
stop := make(chan struct{})
stopped := make(chan struct{})
updateTicker := make(chan struct{})
state := newState(constants.Stopped, settings, start, running, stop, stopped, updateTicker)
return &looper{
state: state{
status: constants.Stopped,
settings: settings,
},
state: state,
conf: conf,
blockBuilder: blacklist.NewBuilder(client),
client: client,
logger: logger,
start: make(chan struct{}),
running: make(chan models.LoopStatus),
stop: make(chan struct{}),
stopped: make(chan struct{}),
updateTicker: make(chan struct{}),
userTrigger: true,
start: start,
running: running,
stop: stop,
stopped: stopped,
updateTicker: updateTicker,
backoffTime: defaultBackoffTime,
timeNow: time.Now,
timeSince: time.Since,
@@ -76,7 +83,7 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
if err != nil {
l.logger.Warn(err)
}
l.logger.Info("attempting restart in %s", l.backoffTime)
l.logger.Info("attempting restart in " + l.backoffTime.String())
timer := time.NewTimer(l.backoffTime)
l.backoffTime *= 2
select {
@@ -88,6 +95,18 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
}
}
func (l *looper) signalOrSetStatus(status models.LoopStatus) {
if l.userTrigger {
l.userTrigger = false
select {
case l.running <- status:
default: // receiver droppped out - avoid deadlock on events routing when shutting down
}
} else {
l.state.SetStatus(status)
}
}
func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
defer close(done)
@@ -101,43 +120,43 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
return
}
crashed := false
l.backoffTime = defaultBackoffTime
for ctx.Err() == nil {
// Upper scope variables for Unbound only
// Their values are to be used if DOT=off
var waitError chan error
var unboundCancel context.CancelFunc
var closeStreams func()
waitError := make(chan error)
unboundCancel := func() { waitError <- nil }
closeStreams := func() {}
for l.GetSettings().Enabled {
var err error
unboundCancel, waitError, closeStreams, err = l.setupUnbound(ctx)
if err == nil {
l.backoffTime = defaultBackoffTime
l.logger.Info("ready")
l.signalOrSetStatus(constants.Running)
break
}
l.signalOrSetStatus(constants.Crashed)
if ctx.Err() != nil {
if !crashed {
l.running <- constants.Stopped
}
return
}
var err error
unboundCancel, waitError, closeStreams, err = l.setupUnbound(ctx, crashed)
if err != nil {
if !errors.Is(err, errUpdateFiles) {
const fallback = true
l.useUnencryptedDNS(fallback)
}
l.logAndWait(ctx, err)
continue
if !errors.Is(err, errUpdateFiles) {
const fallback = true
l.useUnencryptedDNS(fallback)
}
break
l.logAndWait(ctx, err)
}
if !l.GetSettings().Enabled {
const fallback = false
l.useUnencryptedDNS(fallback)
waitError := make(chan error)
unboundCancel = func() { waitError <- nil }
closeStreams = func() {}
}
l.userTrigger = false
stayHere := true
for stayHere {
select {
@@ -148,31 +167,32 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
closeStreams()
return
case <-l.stop:
l.userTrigger = true
l.logger.Info("stopping")
const fallback = false
l.useUnencryptedDNS(fallback)
unboundCancel()
<-waitError
// do not close waitError or the waitError
// select case will trigger
closeStreams()
l.stopped <- struct{}{}
case <-l.start:
l.userTrigger = true
l.logger.Info("starting")
stayHere = false
case err := <-waitError: // unexpected error
close(waitError)
closeStreams()
unboundCancel()
if ctx.Err() != nil {
close(waitError)
closeStreams()
return
}
l.state.setStatusWithLock(constants.Crashed)
l.state.SetStatus(constants.Crashed)
const fallback = true
l.useUnencryptedDNS(fallback)
l.logAndWait(ctx, err)
stayHere = false
}
}
close(waitError)
closeStreams()
}
}
@@ -181,11 +201,10 @@ var errUpdateFiles = errors.New("cannot update files")
// Returning cancel == nil signals we want to re-run setupUnbound
// Returning err == errUpdateFiles signals we should not fall back
// on the plaintext DNS as DOT is still up and running.
func (l *looper) setupUnbound(ctx context.Context, previousCrashed bool) (
func (l *looper) setupUnbound(ctx context.Context) (
cancel context.CancelFunc, waitError chan error, closeStreams func(), err error) {
err = l.updateFiles(ctx)
if err != nil {
l.state.setStatusWithLock(constants.Crashed)
return nil, nil, nil, errUpdateFiles
}
@@ -195,14 +214,16 @@ func (l *looper) setupUnbound(ctx context.Context, previousCrashed bool) (
stdoutLines, stderrLines, waitError, err := l.conf.Start(unboundCtx, settings.Unbound.VerbosityDetailsLevel)
if err != nil {
cancel()
if !previousCrashed {
l.running <- constants.Crashed
}
return nil, nil, nil, err
}
collectLinesDone := make(chan struct{})
go l.collectLines(stdoutLines, stderrLines, collectLinesDone)
closeStreams = func() {
close(stdoutLines)
close(stderrLines)
<-collectLinesDone
}
// use Unbound
nameserver.UseDNSInternally(net.IP{127, 0, 0, 1})
@@ -213,32 +234,13 @@ func (l *looper) setupUnbound(ctx context.Context, previousCrashed bool) (
}
if err := check.WaitForDNS(ctx, net.DefaultResolver); err != nil {
if !previousCrashed {
l.running <- constants.Crashed
}
cancel()
<-waitError
close(waitError)
close(stdoutLines)
close(stderrLines)
<-collectLinesDone
closeStreams()
return nil, nil, nil, err
}
l.logger.Info("ready")
if !previousCrashed {
l.running <- constants.Running
} else {
l.backoffTime = defaultBackoffTime
l.state.setStatusWithLock(constants.Running)
}
closeStreams = func() {
close(stdoutLines)
close(stderrLines)
<-collectLinesDone
}
return cancel, waitError, closeStreams, nil
}
@@ -299,15 +301,15 @@ func (l *looper) RunRestartTicker(ctx context.Context, done chan<- struct{}) {
status := l.GetStatus()
if status == constants.Running {
if err := l.updateFiles(ctx); err != nil {
l.state.setStatusWithLock(constants.Crashed)
l.state.SetStatus(constants.Crashed)
l.logger.Error(err)
l.logger.Warn("skipping Unbound restart due to failed files update")
continue
}
}
_, _ = l.SetStatus(constants.Stopped)
_, _ = l.SetStatus(constants.Running)
_, _ = l.ApplyStatus(ctx, constants.Stopped)
_, _ = l.ApplyStatus(ctx, constants.Running)
settings := l.GetSettings()
timer.Reset(settings.UpdatePeriod)
@@ -353,3 +355,14 @@ func (l *looper) updateFiles(ctx context.Context) (err error) {
return l.conf.MakeUnboundConf(settings.Unbound)
}
func (l *looper) GetStatus() (status models.LoopStatus) { return l.state.GetStatus() }
func (l *looper) ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
return l.state.ApplyStatus(ctx, status)
}
func (l *looper) GetSettings() (settings configuration.DNS) { return l.state.GetSettings() }
func (l *looper) SetSettings(ctx context.Context, settings configuration.DNS) (
outcome string) {
return l.state.SetSettings(ctx, settings)
}

View File

@@ -1,6 +1,7 @@
package dns
import (
"context"
"errors"
"fmt"
"reflect"
@@ -11,92 +12,153 @@ import (
"github.com/qdm12/gluetun/internal/models"
)
type state struct {
status models.LoopStatus
settings configuration.DNS
statusMu sync.RWMutex
settingsMu sync.RWMutex
func newState(status models.LoopStatus, settings configuration.DNS,
start chan<- struct{}, running <-chan models.LoopStatus,
stop chan<- struct{}, stopped <-chan struct{},
updateTicker chan<- struct{}) *state {
return &state{
status: status,
settings: settings,
start: start,
running: running,
stop: stop,
stopped: stopped,
updateTicker: updateTicker,
}
}
func (s *state) setStatusWithLock(status models.LoopStatus) {
type state struct {
loopMu sync.RWMutex
status models.LoopStatus
statusMu sync.RWMutex
settings configuration.DNS
settingsMu sync.RWMutex
start chan<- struct{}
running <-chan models.LoopStatus
stop chan<- struct{}
stopped <-chan struct{}
updateTicker chan<- struct{}
}
func (s *state) Lock() { s.loopMu.Lock() }
func (s *state) Unlock() { s.loopMu.Unlock() }
// SetStatus sets the status thread safely.
// It should only be called by the loop internal code since
// it does not interact with the loop code directly.
func (s *state) SetStatus(status models.LoopStatus) {
s.statusMu.Lock()
defer s.statusMu.Unlock()
s.status = status
}
func (l *looper) GetStatus() (status models.LoopStatus) {
l.state.statusMu.RLock()
defer l.state.statusMu.RUnlock()
return l.state.status
// GetStatus gets the status thread safely.
func (s *state) GetStatus() (status models.LoopStatus) {
s.statusMu.RLock()
defer s.statusMu.RUnlock()
return s.status
}
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
// ApplyStatus sends signals to the running loop depending on the
// current status and status requested, such that its next status
// matches the requested one. It is thread safe and a synchronous call
// since it waits to the loop to fully change its status.
func (s *state) ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
// prevent simultaneous loop changes by restricting
// multiple SetStatus calls to run sequentially.
s.loopMu.Lock()
defer s.loopMu.Unlock()
// not a read lock as we want to modify it eventually in
// the code below before any other call.
s.statusMu.Lock()
existingStatus := s.status
switch status {
case constants.Running:
switch existingStatus {
case constants.Starting, constants.Running, constants.Stopping, constants.Crashed:
return fmt.Sprintf("already %s", existingStatus), nil
if existingStatus != constants.Stopped {
// starting, running, stopping, crashed
s.statusMu.Unlock()
return "already " + existingStatus.String(), nil
}
l.loopLock.Lock()
defer l.loopLock.Unlock()
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
l.state.statusMu.Lock()
l.state.status = newStatus
s.status = constants.Starting
s.statusMu.Unlock()
s.start <- struct{}{}
// Wait for the loop to react to the start signal
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-s.running:
}
s.SetStatus(newStatus)
return newStatus.String(), nil
case constants.Stopped:
switch existingStatus {
case constants.Starting, constants.Stopping, constants.Stopped, constants.Crashed:
return fmt.Sprintf("already %s", existingStatus), nil
if existingStatus != constants.Running {
return "already " + existingStatus.String(), nil
}
l.loopLock.Lock()
defer l.loopLock.Unlock()
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
l.state.statusMu.Lock()
l.state.status = constants.Stopped
return status.String(), nil
s.status = constants.Stopping
s.statusMu.Unlock()
s.stop <- struct{}{}
// Wait for the loop to react to the stop signal
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-s.stopped:
newStatus = constants.Stopped
}
s.SetStatus(newStatus)
return newStatus.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
ErrInvalidStatus, status, constants.Running, constants.Stopped)
}
}
func (l *looper) GetSettings() (settings configuration.DNS) {
l.state.settingsMu.RLock()
defer l.state.settingsMu.RUnlock()
return l.state.settings
func (s *state) GetSettings() (settings configuration.DNS) {
s.settingsMu.RLock()
defer s.settingsMu.RUnlock()
return s.settings
}
func (l *looper) SetSettings(settings configuration.DNS) (outcome string) {
l.state.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
func (s *state) SetSettings(ctx context.Context, settings configuration.DNS) (
outcome string) {
s.settingsMu.Lock()
defer s.settingsMu.Unlock()
settingsUnchanged := reflect.DeepEqual(s.settings, settings)
if settingsUnchanged {
l.state.settingsMu.Unlock()
return "settings left unchanged"
}
tempSettings := l.state.settings
// Check for only update period change
tempSettings := s.settings
tempSettings.UpdatePeriod = settings.UpdatePeriod
onlyUpdatePeriodChanged := reflect.DeepEqual(tempSettings, settings)
l.state.settings = settings
l.state.settingsMu.Unlock()
s.settings = settings
if onlyUpdatePeriodChanged {
l.updateTicker <- struct{}{}
s.updateTicker <- struct{}{}
return "update period changed"
}
_, _ = l.SetStatus(constants.Stopped)
// Restart
_, _ = s.ApplyStatus(ctx, constants.Stopped)
if settings.Enabled {
outcome, _ = l.SetStatus(constants.Running)
outcome, _ = s.ApplyStatus(ctx, constants.Running)
}
return outcome
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"os/exec"
"strings"
"github.com/qdm12/golibs/command"
@@ -15,7 +16,8 @@ var (
)
func ip6tablesSupported(ctx context.Context, commander command.Commander) (supported bool) {
if _, err := commander.Run(ctx, "ip6tables", "-L"); err != nil {
cmd := exec.CommandContext(ctx, "ip6tables", "-L")
if _, err := commander.Run(cmd); err != nil {
return false
}
return true
@@ -40,7 +42,8 @@ func (c *configurator) runIP6tablesInstruction(ctx context.Context, instruction
fmt.Println("ip6tables " + instruction)
}
flags := strings.Fields(instruction)
if output, err := c.commander.Run(ctx, "ip6tables", flags...); err != nil {
cmd := exec.CommandContext(ctx, "ip6tables", flags...)
if output, err := c.commander.Run(cmd); err != nil {
return fmt.Errorf("%w: \"ip6tables %s\": %s: %s", ErrIP6Tables, instruction, output, err)
}
return nil

View File

@@ -7,6 +7,7 @@ import (
"io"
"net"
"os"
"os/exec"
"strings"
"github.com/qdm12/gluetun/internal/models"
@@ -46,7 +47,8 @@ func flipRule(rule string) string {
// Version obtains the version of the installed iptables.
func (c *configurator) Version(ctx context.Context) (string, error) {
output, err := c.commander.Run(ctx, "iptables", "--version")
cmd := exec.CommandContext(ctx, "iptables", "--version")
output, err := c.commander.Run(cmd)
if err != nil {
return "", err
}
@@ -74,7 +76,8 @@ func (c *configurator) runIptablesInstruction(ctx context.Context, instruction s
fmt.Printf("iptables %s\n", instruction)
}
flags := strings.Fields(instruction)
if output, err := c.commander.Run(ctx, "iptables", flags...); err != nil {
cmd := exec.CommandContext(ctx, "iptables", flags...)
if output, err := c.commander.Run(cmd); err != nil {
return fmt.Errorf("%w \"iptables %s\": %s: %s", ErrIPTables, instruction, output, err)
}
return nil

View File

@@ -9,24 +9,24 @@ import (
"time"
)
func (s *server) runHealthcheckLoop(ctx context.Context, healthy chan<- bool, done chan<- struct{}) {
func (s *server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
defer close(done)
s.openvpn.healthyTimer = time.NewTimer(defaultOpenvpnHealthyWaitTime)
for {
previousErr := s.handler.getErr()
err := healthCheck(ctx, s.resolver)
s.handler.setErr(err)
// Notify the healthy channel, or not if it's already full
select {
case healthy <- err == nil:
default:
}
if previousErr != nil && err == nil {
s.logger.Info("healthy!")
s.openvpn.healthyTimer.Stop()
s.openvpn.healthyWaitTime = defaultOpenvpnHealthyWaitTime
} else if previousErr == nil && err != nil {
s.logger.Info("unhealthy: " + err.Error())
s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWaitTime)
}
if err != nil { // try again after 1 second
@@ -38,9 +38,12 @@ func (s *server) runHealthcheckLoop(ctx context.Context, healthy chan<- bool, do
}
return
case <-timer.C:
case <-s.openvpn.healthyTimer.C:
s.onUnhealthyOpenvpn(ctx)
}
continue
}
// Success, check again in 5 seconds
const period = 5 * time.Second
timer := time.NewTimer(period)

View File

@@ -0,0 +1,17 @@
package healthcheck
import (
"context"
"time"
"github.com/qdm12/gluetun/internal/constants"
)
func (s *server) onUnhealthyOpenvpn(ctx context.Context) {
s.logger.Info("program has been unhealthy for " +
s.openvpn.healthyWaitTime.String() + ": restarting OpenVPN")
_, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped)
_, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Running)
s.openvpn.healthyWaitTime += openvpnHealthyWaitTimeAdd
s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWaitTime)
}

View File

@@ -7,11 +7,12 @@ import (
"net/http"
"time"
"github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/golibs/logging"
)
type Server interface {
Run(ctx context.Context, healthy chan<- bool, done chan<- struct{})
Run(ctx context.Context, done chan<- struct{})
}
type server struct {
@@ -19,22 +20,40 @@ type server struct {
logger logging.Logger
handler *handler
resolver *net.Resolver
openvpn openvpnHealth
}
func NewServer(address string, logger logging.Logger) Server {
type openvpnHealth struct {
looper openvpn.Looper
healthyWaitTime time.Duration
healthyTimer *time.Timer
}
const (
defaultOpenvpnHealthyWaitTime = 6 * time.Second
openvpnHealthyWaitTimeAdd = 5 * time.Second
)
func NewServer(address string, logger logging.Logger,
openvpnLooper openvpn.Looper) Server {
return &server{
address: address,
logger: logger,
handler: newHandler(logger),
resolver: net.DefaultResolver,
openvpn: openvpnHealth{
looper: openvpnLooper,
healthyWaitTime: defaultOpenvpnHealthyWaitTime,
},
}
}
func (s *server) Run(ctx context.Context, healthy chan<- bool, done chan<- struct{}) {
func (s *server) Run(ctx context.Context, done chan<- struct{}) {
defer close(done)
s.logger.Debug("here 0")
loopDone := make(chan struct{})
go s.runHealthcheckLoop(ctx, healthy, loopDone)
go s.runHealthcheckLoop(ctx, loopDone)
server := http.Server{
Addr: s.address,

View File

@@ -15,10 +15,12 @@ import (
type Looper interface {
Run(ctx context.Context, done chan<- struct{})
SetStatus(status models.LoopStatus) (outcome string, err error)
SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetStatus() (status models.LoopStatus)
GetSettings() (settings configuration.HTTPProxy)
SetSettings(settings configuration.HTTPProxy) (outcome string)
SetSettings(ctx context.Context, settings configuration.HTTPProxy) (
outcome string)
}
type looper struct {
@@ -57,7 +59,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
if l.GetSettings().Enabled {
go func() {
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Running)
}()
}

View File

@@ -1,6 +1,7 @@
package httpproxy
import (
"context"
"errors"
"fmt"
"reflect"
@@ -32,7 +33,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) {
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
@@ -48,7 +50,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-l.running:
}
l.state.statusMu.Lock()
l.state.status = newStatus
return newStatus.String(), nil
@@ -62,9 +69,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-l.stopped:
newStatus = constants.Stopped
}
l.state.statusMu.Lock()
l.state.status = status
l.state.status = newStatus
return status.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
@@ -78,7 +91,8 @@ func (l *looper) GetSettings() (settings configuration.HTTPProxy) {
return l.state.settings
}
func (l *looper) SetSettings(settings configuration.HTTPProxy) (outcome string) {
func (l *looper) SetSettings(ctx context.Context, settings configuration.HTTPProxy) (
outcome string) {
l.state.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)
if settingsUnchanged {
@@ -93,12 +107,12 @@ func (l *looper) SetSettings(settings configuration.HTTPProxy) (outcome string)
switch {
case !newEnabled && !previousEnabled:
case newEnabled && previousEnabled:
_, _ = l.SetStatus(constants.Stopped)
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Stopped)
_, _ = l.SetStatus(ctx, constants.Running)
case newEnabled && !previousEnabled:
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Running)
case !newEnabled && previousEnabled:
_, _ = l.SetStatus(constants.Stopped)
_, _ = l.SetStatus(ctx, constants.Stopped)
}
return "settings updated"
}

View File

@@ -4,7 +4,9 @@ import (
"context"
"errors"
"fmt"
"os/exec"
"strings"
"syscall"
"github.com/qdm12/gluetun/internal/constants"
)
@@ -30,7 +32,10 @@ func (c *configurator) Start(ctx context.Context, version string) (
c.logger.Info("starting OpenVPN " + version)
return c.commander.Start(ctx, bin, "--config", constants.OpenVPNConf)
cmd := exec.CommandContext(ctx, bin, "--config", constants.OpenVPNConf)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
return c.commander.Start(cmd)
}
func (c *configurator) Version24(ctx context.Context) (version string, err error) {
@@ -44,7 +49,8 @@ func (c *configurator) Version25(ctx context.Context) (version string, err error
var ErrVersionTooShort = errors.New("version output is too short")
func (c *configurator) version(ctx context.Context, binName string) (version string, err error) {
output, err := c.commander.Run(ctx, binName, "--version")
cmd := exec.CommandContext(ctx, binName, "--version")
output, err := c.commander.Run(cmd)
if err != nil && err.Error() != "exit status 1" {
return "", err
}

View File

@@ -72,8 +72,6 @@ func processLogLine(s string) (filtered string, level logging.Level) {
Your credentials might be wrong 🤨
💡 If you use Private Internet Access, check https://github.com/qdm12/gluetun/issues/265
`
level = logging.LevelError
case strings.Contains(s, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"): //nolint:lll

View File

@@ -42,7 +42,7 @@ func Test_processLogLine(t *testing.T) {
logging.LevelInfo},
"openvpn auth failed": {
"AUTH: Received control message: AUTH_FAILED",
"AUTH: Received control message: AUTH_FAILED\n\nYour credentials might be wrong 🤨\n\n💡 If you use Private Internet Access, check https://github.com/qdm12/gluetun/issues/265\n\n", //nolint:lll
"AUTH: Received control message: AUTH_FAILED\n\nYour credentials might be wrong 🤨\n\n",
logging.LevelError},
}
for name, tc := range tests {

View File

@@ -5,7 +5,6 @@ import (
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/qdm12/gluetun/internal/configuration"
@@ -21,9 +20,11 @@ import (
type Looper interface {
Run(ctx context.Context, done chan<- struct{})
GetStatus() (status models.LoopStatus)
SetStatus(status models.LoopStatus) (outcome string, err error)
ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetSettings() (settings configuration.OpenVPN)
SetSettings(settings configuration.OpenVPN) (outcome string)
SetSettings(ctx context.Context, settings configuration.OpenVPN) (
outcome string)
GetServers() (servers models.AllServers)
SetServers(servers models.AllServers)
GetPortForwarded() (port uint16)
@@ -31,7 +32,7 @@ type Looper interface {
}
type looper struct {
state state
state *state
// Fixed parameters
username string
puid int
@@ -45,34 +46,36 @@ type looper struct {
client *http.Client
openFile os.OpenFileFunc
tunnelReady chan<- struct{}
healthy <-chan bool
// Internal channels and locks
loopLock sync.Mutex
running chan models.LoopStatus
stop, stopped chan struct{}
start chan struct{}
// Internal channels and values
stop <-chan struct{}
stopped chan<- struct{}
start <-chan struct{}
running chan<- models.LoopStatus
portForwardSignals chan net.IP
crashed bool
backoffTime time.Duration
healthWaitTime time.Duration
userTrigger bool
// Internal constant values
backoffTime time.Duration
}
const (
defaultBackoffTime = 15 * time.Second
defaultHealthWaitTime = 6 * time.Second
defaultBackoffTime = 15 * time.Second
)
func NewLooper(settings configuration.OpenVPN,
username string, puid, pgid int, allServers models.AllServers,
conf Configurator, fw firewall.Configurator, routing routing.Routing,
logger logging.ParentLogger, client *http.Client, openFile os.OpenFileFunc,
tunnelReady chan<- struct{}, healthy <-chan bool) Looper {
tunnelReady chan<- struct{}) Looper {
start := make(chan struct{})
running := make(chan models.LoopStatus)
stop := make(chan struct{})
stopped := make(chan struct{})
state := newState(constants.Stopped, settings, allServers,
start, running, stop, stopped)
return &looper{
state: state{
status: constants.Stopped,
settings: settings,
allServers: allServers,
},
state: state,
username: username,
puid: puid,
pgid: pgid,
@@ -84,28 +87,33 @@ func NewLooper(settings configuration.OpenVPN,
client: client,
openFile: openFile,
tunnelReady: tunnelReady,
healthy: healthy,
start: make(chan struct{}),
running: make(chan models.LoopStatus),
stop: make(chan struct{}),
stopped: make(chan struct{}),
start: start,
running: running,
stop: stop,
stopped: stopped,
portForwardSignals: make(chan net.IP),
userTrigger: true,
backoffTime: defaultBackoffTime,
healthWaitTime: defaultHealthWaitTime,
}
}
func (l *looper) PortForward(vpnGateway net.IP) { l.portForwardSignals <- vpnGateway }
func (l *looper) signalCrashedStatus() {
if !l.crashed {
l.crashed = true
l.running <- constants.Crashed
func (l *looper) signalOrSetStatus(status models.LoopStatus) {
if l.userTrigger {
l.userTrigger = false
select {
case l.running <- status:
default: // receiver calling ApplyStatus droppped out
}
} else {
l.state.SetStatus(status)
}
}
func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocognit
func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
defer close(done)
select {
case <-l.start:
case <-ctx.Done():
@@ -113,17 +121,17 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocog
}
for ctx.Err() == nil {
settings, allServers := l.state.getSettingsAndServers()
settings, allServers := l.state.GetSettingsAndServers()
providerConf := provider.New(settings.Provider.Name, allServers, time.Now)
var connection models.OpenVPNConnection
var lines []string
var err error
if len(settings.Config) == 0 {
if settings.Config == "" {
connection, err = providerConf.GetOpenVPNConnection(settings.Provider.ServerSelection)
if err != nil {
l.signalCrashedStatus()
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
@@ -131,28 +139,30 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocog
} else {
lines, connection, err = l.processCustomConfig(settings)
if err != nil {
l.signalCrashedStatus()
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
}
if err := writeOpenvpnConf(lines, l.openFile); err != nil {
l.signalCrashedStatus()
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
if settings.User != "" {
if err := l.conf.WriteAuthFile(settings.User, settings.Password, l.puid, l.pgid); err != nil {
l.signalCrashedStatus()
err := l.conf.WriteAuthFile(
settings.User, settings.Password, l.puid, l.pgid)
if err != nil {
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
}
if err := l.fw.SetVPNConnection(ctx, connection); err != nil {
l.signalCrashedStatus()
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
@@ -162,13 +172,18 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocog
stdoutLines, stderrLines, waitError, err := l.conf.Start(openvpnCtx, settings.Version)
if err != nil {
openvpnCancel()
l.signalCrashedStatus()
l.signalOrSetStatus(constants.Crashed)
l.logAndWait(ctx, err)
continue
}
lineCollectionDone := make(chan struct{})
go l.collectLines(stdoutLines, stderrLines, lineCollectionDone)
closeStreams := func() {
close(stdoutLines)
close(stderrLines)
<-lineCollectionDone
}
// Needs the stream line from main.go to know when the tunnel is up
portForwardDone := make(chan struct{})
@@ -183,13 +198,8 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocog
}
}(openvpnCtx)
if l.crashed {
l.crashed = false
l.backoffTime = defaultBackoffTime
l.state.setStatusWithLock(constants.Running)
} else {
l.running <- constants.Running
}
l.backoffTime = defaultBackoffTime
l.signalOrSetStatus(constants.Running)
stayHere := true
for stayHere {
@@ -198,55 +208,39 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { //nolint:gocog
openvpnCancel()
<-waitError
close(waitError)
close(stdoutLines)
close(stderrLines)
<-lineCollectionDone
closeStreams()
<-portForwardDone
return
case <-l.stop:
l.userTrigger = true
l.logger.Info("stopping")
openvpnCancel()
<-waitError
// do not close waitError or the waitError
// select case will trigger
closeStreams()
<-portForwardDone
l.stopped <- struct{}{}
case <-l.start:
l.userTrigger = true
l.logger.Info("starting")
stayHere = false
case err := <-waitError: // unexpected error
close(waitError)
closeStreams()
l.state.Lock() // prevent SetStatus from running in parallel
openvpnCancel()
if ctx.Err() != nil {
close(waitError)
close(stdoutLines)
close(stderrLines)
<-lineCollectionDone
<-portForwardDone
return
}
l.state.setStatusWithLock(constants.Crashed)
l.state.SetStatus(constants.Crashed)
<-portForwardDone
l.logAndWait(ctx, err)
l.crashed = true
stayHere = false
case healthy := <-l.healthy:
if healthy {
continue
}
// ensure it stays unhealthy for some time before restarting it
healthy = l.waitForHealth(ctx)
if healthy || ctx.Err() != nil {
continue
}
l.crashed = true // flag as crashed
l.state.setStatusWithLock(constants.Stopping)
l.logger.Warn("unhealthy program: restarting openvpn")
openvpnCancel()
<-waitError
l.state.setStatusWithLock(constants.Stopped)
stayHere = false
l.state.Unlock()
}
}
close(waitError)
close(stdoutLines)
close(stderrLines)
openvpnCancel() // just for the linter
openvpnCancel()
}
}
@@ -254,7 +248,7 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
if err != nil {
l.logger.Error(err)
}
l.logger.Info("retrying in %s", l.backoffTime)
l.logger.Info("retrying in " + l.backoffTime.String())
timer := time.NewTimer(l.backoffTime)
l.backoffTime *= 2
select {
@@ -266,35 +260,6 @@ func (l *looper) logAndWait(ctx context.Context, err error) {
}
}
// waitForHealth waits for a true healthy signal
// after restarting openvpn in order to avoid restarting
// openvpn in a loop as it requires a few seconds to connect.
func (l *looper) waitForHealth(ctx context.Context) (healthy bool) {
l.logger.Info("unhealthy program: waiting %s for it to change to healthy", l.healthWaitTime)
timer := time.NewTimer(l.healthWaitTime)
l.healthWaitTime *= 2
for {
select {
case healthy = <-l.healthy:
if !healthy {
break
}
if !timer.Stop() {
<-timer.C
}
l.healthWaitTime = defaultHealthWaitTime
return true
case <-timer.C:
return false
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return false
}
}
}
// portForward is a blocking operation which may or may not be infinite.
// You should therefore always call it in a goroutine.
func (l *looper) portForward(ctx context.Context,
@@ -329,3 +294,27 @@ func writeOpenvpnConf(lines []string, openFile os.OpenFileFunc) error {
}
return file.Close()
}
func (l *looper) GetStatus() (status models.LoopStatus) {
return l.state.GetStatus()
}
func (l *looper) ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
return l.state.ApplyStatus(ctx, status)
}
func (l *looper) GetSettings() (settings configuration.OpenVPN) {
return l.state.GetSettings()
}
func (l *looper) SetSettings(ctx context.Context, settings configuration.OpenVPN) (
outcome string) {
return l.state.SetSettings(ctx, settings)
}
func (l *looper) GetServers() (servers models.AllServers) {
return l.state.GetServers()
}
func (l *looper) SetServers(servers models.AllServers) {
l.state.SetServers(servers)
}
func (l *looper) GetPortForwarded() (port uint16) {
return l.state.GetPortForwarded()
}

View File

@@ -1,6 +1,7 @@
package openvpn
import (
"context"
"errors"
"fmt"
"reflect"
@@ -11,24 +12,63 @@ import (
"github.com/qdm12/gluetun/internal/models"
)
type state struct {
status models.LoopStatus
settings configuration.OpenVPN
allServers models.AllServers
portForwarded uint16
statusMu sync.RWMutex
settingsMu sync.RWMutex
allServersMu sync.RWMutex
portForwardedMu sync.RWMutex
func newState(status models.LoopStatus,
settings configuration.OpenVPN, allServers models.AllServers,
start chan<- struct{}, running <-chan models.LoopStatus,
stop chan<- struct{}, stopped <-chan struct{}) *state {
return &state{
status: status,
settings: settings,
allServers: allServers,
start: start,
running: running,
stop: stop,
stopped: stopped,
}
}
func (s *state) setStatusWithLock(status models.LoopStatus) {
type state struct {
loopMu sync.RWMutex
status models.LoopStatus
statusMu sync.RWMutex
settings configuration.OpenVPN
settingsMu sync.RWMutex
allServers models.AllServers
allServersMu sync.RWMutex
portForwarded uint16
portForwardedMu sync.RWMutex
start chan<- struct{}
running <-chan models.LoopStatus
stop chan<- struct{}
stopped <-chan struct{}
}
func (s *state) Lock() { s.loopMu.Lock() }
func (s *state) Unlock() { s.loopMu.Unlock() }
// SetStatus sets the status thread safely.
// It should only be called by the loop internal code since
// it does not interact with the loop code directly.
func (s *state) SetStatus(status models.LoopStatus) {
s.statusMu.Lock()
defer s.statusMu.Unlock()
s.status = status
}
func (s *state) getSettingsAndServers() (settings configuration.OpenVPN, allServers models.AllServers) {
// GetStatus gets the status thread safely.
func (s *state) GetStatus() (status models.LoopStatus) {
s.statusMu.RLock()
defer s.statusMu.RUnlock()
return s.status
}
func (s *state) GetSettingsAndServers() (settings configuration.OpenVPN,
allServers models.AllServers) {
s.settingsMu.RLock()
s.allServersMu.RLock()
settings = s.settings
@@ -38,87 +78,102 @@ func (s *state) getSettingsAndServers() (settings configuration.OpenVPN, allServ
return settings, allServers
}
func (l *looper) GetStatus() (status models.LoopStatus) {
l.state.statusMu.RLock()
defer l.state.statusMu.RUnlock()
return l.state.status
}
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
// ApplyStatus sends signals to the running loop depending on the
// current status and status requested, such that its next status
// matches the requested one. It is thread safe and a synchronous call
// since it waits to the loop to fully change its status.
func (s *state) ApplyStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
// prevent simultaneous loop changes by restricting
// multiple SetStatus calls to run sequentially.
s.loopMu.Lock()
defer s.loopMu.Unlock()
// not a read lock as we want to modify it eventually in
// the code below before any other call.
s.statusMu.Lock()
existingStatus := s.status
switch status {
case constants.Running:
switch existingStatus {
case constants.Starting, constants.Running, constants.Stopping, constants.Crashed:
return fmt.Sprintf("already %s", existingStatus), nil
if existingStatus != constants.Stopped {
return "already " + existingStatus.String(), nil
}
l.loopLock.Lock()
defer l.loopLock.Unlock()
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
l.state.statusMu.Lock()
l.state.status = newStatus
s.status = constants.Starting
s.statusMu.Unlock()
s.start <- struct{}{}
// Wait for the loop to react to the start signal
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-s.running:
}
s.SetStatus(newStatus)
return newStatus.String(), nil
case constants.Stopped:
switch existingStatus {
case constants.Starting, constants.Stopping, constants.Stopped, constants.Crashed:
return fmt.Sprintf("already %s", existingStatus), nil
if existingStatus != constants.Running {
return "already " + existingStatus.String(), nil
}
l.loopLock.Lock()
defer l.loopLock.Unlock()
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
l.state.statusMu.Lock()
l.state.status = constants.Stopped
return status.String(), nil
s.status = constants.Stopping
s.statusMu.Unlock()
s.stop <- struct{}{}
// Wait for the loop to react to the stop signal
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-s.stopped:
newStatus = constants.Stopped
}
s.SetStatus(newStatus)
return newStatus.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
ErrInvalidStatus, status, constants.Running, constants.Stopped)
}
}
func (l *looper) GetSettings() (settings configuration.OpenVPN) {
l.state.settingsMu.RLock()
defer l.state.settingsMu.RUnlock()
return l.state.settings
func (s *state) GetSettings() (settings configuration.OpenVPN) {
s.settingsMu.RLock()
defer s.settingsMu.RUnlock()
return s.settings
}
func (l *looper) SetSettings(settings configuration.OpenVPN) (outcome string) {
l.state.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(l.state.settings, settings)
func (s *state) SetSettings(ctx context.Context, settings configuration.OpenVPN) (
outcome string) {
s.settingsMu.Lock()
defer s.settingsMu.Unlock()
settingsUnchanged := reflect.DeepEqual(s.settings, settings)
if settingsUnchanged {
l.state.settingsMu.Unlock()
return "settings left unchanged"
}
l.state.settings = settings
_, _ = l.SetStatus(constants.Stopped)
outcome, _ = l.SetStatus(constants.Running)
s.settings = settings
_, _ = s.ApplyStatus(ctx, constants.Stopped)
outcome, _ = s.ApplyStatus(ctx, constants.Running)
return outcome
}
func (l *looper) GetServers() (servers models.AllServers) {
l.state.allServersMu.RLock()
defer l.state.allServersMu.RUnlock()
return l.state.allServers
func (s *state) GetServers() (servers models.AllServers) {
s.allServersMu.RLock()
defer s.allServersMu.RUnlock()
return s.allServers
}
func (l *looper) SetServers(servers models.AllServers) {
l.state.allServersMu.Lock()
defer l.state.allServersMu.Unlock()
l.state.allServers = servers
func (s *state) SetServers(servers models.AllServers) {
s.allServersMu.Lock()
defer s.allServersMu.Unlock()
s.allServers = servers
}
func (l *looper) GetPortForwarded() (port uint16) {
l.state.portForwardedMu.RLock()
defer l.state.portForwardedMu.RUnlock()
return l.state.portForwarded
func (s *state) GetPortForwarded() (port uint16) {
s.portForwardedMu.RLock()
defer s.portForwardedMu.RUnlock()
return s.portForwarded
}

View File

@@ -68,6 +68,13 @@ func (c *Cyberghost) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.CyberghostCertificate)...)
lines = append(lines, utils.WrapOpenvpnCert(

View File

@@ -62,6 +62,13 @@ func (f *Fastestvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.FastestvpnCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -59,6 +59,13 @@ func (h *HideMyAss) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.HideMyAssCA)...)
lines = append(lines, utils.WrapOpenvpnCert(

View File

@@ -57,6 +57,13 @@ func (i *Ipvanish) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(constants.IpvanishCA)...)
lines = append(lines, "")

View File

@@ -63,6 +63,13 @@ func (i *Ivpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.IvpnCA)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -71,6 +71,13 @@ func (m *Mullvad) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.MullvadCertificate)...)

View File

@@ -67,6 +67,13 @@ func (n *Nordvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.NordvpnCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -58,6 +58,13 @@ func (p *Privado) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.PrivadoCertificate)...)

View File

@@ -81,6 +81,13 @@ func (p *PIA) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(certificate)...)
lines = append(lines, utils.WrapOpenvpnCRLVerify(X509CRL)...)

View File

@@ -29,7 +29,6 @@ func (p *Privatevpn) BuildConf(connection models.OpenVPNConnection,
// Privatevpn specific
"comp-lzo",
"tun-ipv6",
// Added constant values
"auth-nocache",
@@ -60,6 +59,13 @@ func (p *Privatevpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.PrivatevpnCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSCrypt(

View File

@@ -66,6 +66,13 @@ func (p *Protonvpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.ProtonvpnCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -66,6 +66,13 @@ func (p *Purevpn) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.PurevpnCertificateAuthority)...)
lines = append(lines, utils.WrapOpenvpnCert(

View File

@@ -64,6 +64,13 @@ func (s *Surfshark) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.SurfsharkCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -70,6 +70,13 @@ func (t *Torguard) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "fast-io")
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.TorguardCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -35,7 +35,6 @@ func (p *Provider) BuildConf(connection models.OpenVPNConnection,
// Modified variables
"verb " + strconv.Itoa(settings.Verbosity),
// "auth-user-pass " + constants.OpenVPNAuthConf,
connection.ProtoLine(),
connection.RemoteLine(),
}
@@ -56,6 +55,13 @@ func (p *Provider) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "user "+username)
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.VPNUnlimitedCertificateAuthority)...)
lines = append(lines, utils.WrapOpenvpnCert(

View File

@@ -67,6 +67,13 @@ func (w *Windscribe) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
}
if settings.Provider.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, "tun-ipv6")
} else {
lines = append(lines, `pull-filter ignore "route-ipv6"`)
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
}
lines = append(lines, utils.WrapOpenvpnCA(
constants.WindscribeCertificate)...)
lines = append(lines, utils.WrapOpenvpnTLSAuth(

View File

@@ -18,7 +18,8 @@ type Looper interface {
Run(ctx context.Context, done chan<- struct{})
RunRestartTicker(ctx context.Context, done chan<- struct{})
GetStatus() (status models.LoopStatus)
SetStatus(status models.LoopStatus) (outcome string, err error)
SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetSettings() (settings configuration.PublicIP)
SetSettings(settings configuration.PublicIP) (outcome string)
GetPublicIP() (publicIP net.IP)

View File

@@ -1,6 +1,7 @@
package publicip
import (
"context"
"errors"
"fmt"
"net"
@@ -35,7 +36,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) {
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
@@ -51,7 +53,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-l.running:
}
l.state.statusMu.Lock()
l.state.status = newStatus
return newStatus.String(), nil
@@ -65,9 +72,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-l.stopped:
newStatus = constants.Stopped
}
l.state.statusMu.Lock()
l.state.status = status
l.state.status = newStatus
return status.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
@@ -81,7 +94,8 @@ func (l *looper) GetSettings() (settings configuration.PublicIP) {
return l.state.settings
}
func (l *looper) SetSettings(settings configuration.PublicIP) (outcome string) {
func (l *looper) SetSettings(settings configuration.PublicIP) (
outcome string) {
l.state.settingsMu.Lock()
defer l.state.settingsMu.Unlock()
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"encoding/json"
"net/http"
"strings"
@@ -9,14 +10,17 @@ import (
"github.com/qdm12/golibs/logging"
)
func newDNSHandler(looper dns.Looper, logger logging.Logger) http.Handler {
func newDNSHandler(ctx context.Context, looper dns.Looper,
logger logging.Logger) http.Handler {
return &dnsHandler{
ctx: ctx,
looper: looper,
logger: logger,
}
}
type dnsHandler struct {
ctx context.Context
looper dns.Looper
logger logging.Logger
}
@@ -61,7 +65,7 @@ func (h *dnsHandler) setStatus(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
outcome, err := h.looper.SetStatus(status)
outcome, err := h.looper.ApplyStatus(h.ctx, status)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"net/http"
"strings"
@@ -12,7 +13,7 @@ import (
"github.com/qdm12/golibs/logging"
)
func newHandler(logger logging.Logger, logging bool,
func newHandler(ctx context.Context, logger logging.Logger, logging bool,
buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper,
unboundLooper dns.Looper,
@@ -21,12 +22,12 @@ func newHandler(logger logging.Logger, logging bool,
) http.Handler {
handler := &handler{}
openvpn := newOpenvpnHandler(openvpnLooper, logger)
dns := newDNSHandler(unboundLooper, logger)
updater := newUpdaterHandler(updaterLooper, logger)
openvpn := newOpenvpnHandler(ctx, openvpnLooper, logger)
dns := newDNSHandler(ctx, unboundLooper, logger)
updater := newUpdaterHandler(ctx, updaterLooper, logger)
publicip := newPublicIPHandler(publicIPLooper, logger)
handler.v0 = newHandlerV0(logger, openvpnLooper, unboundLooper, updaterLooper)
handler.v0 = newHandlerV0(ctx, logger, openvpnLooper, unboundLooper, updaterLooper)
handler.v1 = newHandlerV1(logger, buildInfo, openvpn, dns, updater, publicip)
handlerWithLog := withLogMiddleware(handler, logger, logging)

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"net/http"
"github.com/qdm12/gluetun/internal/constants"
@@ -10,9 +11,10 @@ import (
"github.com/qdm12/golibs/logging"
)
func newHandlerV0(logger logging.Logger,
func newHandlerV0(ctx context.Context, logger logging.Logger,
openvpn openvpn.Looper, dns dns.Looper, updater updater.Looper) http.Handler {
return &handlerV0{
ctx: ctx,
logger: logger,
openvpn: openvpn,
dns: dns,
@@ -21,6 +23,7 @@ func newHandlerV0(logger logging.Logger,
}
type handlerV0 struct {
ctx context.Context
logger logging.Logger
openvpn openvpn.Looper
dns dns.Looper
@@ -36,17 +39,17 @@ func (h *handlerV0) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "/version":
http.Redirect(w, r, "/v1/version", http.StatusPermanentRedirect)
case "/openvpn/actions/restart":
outcome, _ := h.openvpn.SetStatus(constants.Stopped)
outcome, _ := h.openvpn.ApplyStatus(h.ctx, constants.Stopped)
h.logger.Info("openvpn: %s", outcome)
outcome, _ = h.openvpn.SetStatus(constants.Running)
outcome, _ = h.openvpn.ApplyStatus(h.ctx, constants.Running)
h.logger.Info("openvpn: %s", outcome)
if _, err := w.Write([]byte("openvpn restarted, please consider using the /v1/ API in the future.")); err != nil {
h.logger.Warn(err)
}
case "/unbound/actions/restart":
outcome, _ := h.dns.SetStatus(constants.Stopped)
outcome, _ := h.dns.ApplyStatus(h.ctx, constants.Stopped)
h.logger.Info("dns: %s", outcome)
outcome, _ = h.dns.SetStatus(constants.Running)
outcome, _ = h.dns.ApplyStatus(h.ctx, constants.Running)
h.logger.Info("dns: %s", outcome)
if _, err := w.Write([]byte("dns restarted, please consider using the /v1/ API in the future.")); err != nil {
h.logger.Warn(err)
@@ -56,9 +59,9 @@ func (h *handlerV0) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "/openvpn/settings":
http.Redirect(w, r, "/v1/openvpn/settings", http.StatusPermanentRedirect)
case "/updater/restart":
outcome, _ := h.updater.SetStatus(constants.Stopped)
outcome, _ := h.updater.SetStatus(h.ctx, constants.Stopped)
h.logger.Info("updater: %s", outcome)
outcome, _ = h.updater.SetStatus(constants.Running)
outcome, _ = h.updater.SetStatus(h.ctx, constants.Running)
h.logger.Info("updater: %s", outcome)
if _, err := w.Write([]byte("updater restarted, please consider using the /v1/ API in the future.")); err != nil {
h.logger.Warn(err)

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"encoding/json"
"net/http"
"strings"
@@ -9,14 +10,17 @@ import (
"github.com/qdm12/golibs/logging"
)
func newOpenvpnHandler(looper openvpn.Looper, logger logging.Logger) http.Handler {
func newOpenvpnHandler(ctx context.Context, looper openvpn.Looper,
logger logging.Logger) http.Handler {
return &openvpnHandler{
ctx: ctx,
looper: looper,
logger: logger,
}
}
type openvpnHandler struct {
ctx context.Context
looper openvpn.Looper
logger logging.Logger
}
@@ -75,7 +79,7 @@ func (h *openvpnHandler) setStatus(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
outcome, err := h.looper.SetStatus(status)
outcome, err := h.looper.ApplyStatus(h.ctx, status)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View File

@@ -25,11 +25,11 @@ type server struct {
handler http.Handler
}
func New(address string, logEnabled bool, logger logging.Logger,
func New(ctx context.Context, address string, logEnabled bool, logger logging.Logger,
buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper, unboundLooper dns.Looper,
updaterLooper updater.Looper, publicIPLooper publicip.Looper) Server {
handler := newHandler(logger, logEnabled, buildInfo,
handler := newHandler(ctx, logger, logEnabled, buildInfo,
openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
return &server{
address: address,

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"encoding/json"
"net/http"
"strings"
@@ -10,15 +11,18 @@ import (
)
func newUpdaterHandler(
ctx context.Context,
looper updater.Looper,
logger logging.Logger) http.Handler {
return &updaterHandler{
ctx: ctx,
looper: looper,
logger: logger,
}
}
type updaterHandler struct {
ctx context.Context
looper updater.Looper
logger logging.Logger
}
@@ -63,7 +67,7 @@ func (h *updaterHandler) setStatus(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
outcome, err := h.looper.SetStatus(status)
outcome, err := h.looper.SetStatus(h.ctx, status)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View File

@@ -16,10 +16,12 @@ import (
type Looper interface {
Run(ctx context.Context, done chan<- struct{})
SetStatus(status models.LoopStatus) (outcome string, err error)
SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetStatus() (status models.LoopStatus)
GetSettings() (settings configuration.ShadowSocks)
SetSettings(settings configuration.ShadowSocks) (outcome string)
SetSettings(ctx context.Context, settings configuration.ShadowSocks) (
outcome string)
}
type looper struct {
@@ -74,7 +76,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) {
if l.GetSettings().Enabled {
go func() {
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Running)
}()
}

View File

@@ -1,6 +1,7 @@
package shadowsocks
import (
"context"
"errors"
"fmt"
"reflect"
@@ -32,7 +33,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) {
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
@@ -48,7 +50,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-l.running:
}
l.state.statusMu.Lock()
l.state.status = newStatus
return newStatus.String(), nil
@@ -62,9 +69,14 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-l.stopped:
newStatus = constants.Stopped
}
l.state.statusMu.Lock()
l.state.status = status
l.state.status = newStatus
return status.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
@@ -78,7 +90,8 @@ func (l *looper) GetSettings() (settings configuration.ShadowSocks) {
return l.state.settings
}
func (l *looper) SetSettings(settings configuration.ShadowSocks) (outcome string) {
func (l *looper) SetSettings(ctx context.Context, settings configuration.ShadowSocks) (
outcome string) {
l.state.settingsMu.Lock()
settingsUnchanged := reflect.DeepEqual(settings, l.state.settings)
if settingsUnchanged {
@@ -93,12 +106,12 @@ func (l *looper) SetSettings(settings configuration.ShadowSocks) (outcome string
switch {
case !newEnabled && !previousEnabled:
case newEnabled && previousEnabled:
_, _ = l.SetStatus(constants.Stopped)
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Stopped)
_, _ = l.SetStatus(ctx, constants.Running)
case newEnabled && !previousEnabled:
_, _ = l.SetStatus(constants.Running)
_, _ = l.SetStatus(ctx, constants.Running)
case !newEnabled && previousEnabled:
_, _ = l.SetStatus(constants.Stopped)
_, _ = l.SetStatus(ctx, constants.Stopped)
}
return "settings updated"
}

View File

@@ -17,7 +17,8 @@ type Looper interface {
Run(ctx context.Context, done chan<- struct{})
RunRestartTicker(ctx context.Context, done chan<- struct{})
GetStatus() (status models.LoopStatus)
SetStatus(status models.LoopStatus) (outcome string, err error)
SetStatus(ctx context.Context, status models.LoopStatus) (
outcome string, err error)
GetSettings() (settings configuration.Updater)
SetSettings(settings configuration.Updater) (outcome string)
}

View File

@@ -1,6 +1,7 @@
package updater
import (
"context"
"errors"
"fmt"
"reflect"
@@ -32,7 +33,7 @@ func (l *looper) GetStatus() (status models.LoopStatus) {
var ErrInvalidStatus = errors.New("invalid status")
func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) {
func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) (outcome string, err error) {
l.state.statusMu.Lock()
defer l.state.statusMu.Unlock()
existingStatus := l.state.status
@@ -48,7 +49,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Starting
l.state.statusMu.Unlock()
l.start <- struct{}{}
newStatus := <-l.running
newStatus := constants.Starting // for canceled context
select {
case <-ctx.Done():
case newStatus = <-l.running:
}
l.state.statusMu.Lock()
l.state.status = newStatus
return newStatus.String(), nil
@@ -62,9 +68,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error)
l.state.status = constants.Stopping
l.state.statusMu.Unlock()
l.stop <- struct{}{}
<-l.stopped
newStatus := constants.Stopping // for canceled context
select {
case <-ctx.Done():
case <-l.stopped:
newStatus = constants.Stopped
}
l.state.statusMu.Lock()
l.state.status = status
l.state.status = newStatus
return status.String(), nil
default:
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",