Compare commits

..

18 Commits

Author SHA1 Message Date
Quentin McGaw 78f6076c83 chore(restrictednet): internal/restrictednet package (#3361) 2026-07-19 16:06:48 +02:00
Quentin McGaw 03d0838356 chore(tests): migrate to use go.uber.org/mock instead of github.com/golang/mock 2026-07-19 13:22:29 +00:00
Quentin McGaw e7b304d0b4 chore(dev): bump dev container from v0.21 to v0.22 - notable changes
- Go upgraded from 1.25 to 1.26
- `golangci-lint` upgraded from v2.4.0 to v2.11.4
- `mockgen` and `gomock` upgraded from `golang/mock` to `uber-go/mock` v0.6.0
2026-07-19 13:15:39 +00:00
Quentin McGaw 93cc5a4b2c chore(dns): deprecate BLOCK_SURVEILLANCE 2026-06-29 11:22:50 +00:00
dependabot[bot] a17591dcdb Chore(deps): Bump github.com/mdlayher/genetlink from 1.3.2 to 1.4.0 (#3348) 2026-06-24 21:51:14 +02:00
dependabot[bot] c31c566282 Chore(deps): Bump golang.org/x/text from 0.37.0 to 0.38.0 (#3362) 2026-06-24 21:50:47 +02:00
dependabot[bot] 496458ca4e Chore(deps): Bump github.com/amnezia-vpn/amneziawg-go from 0.2.16 to 0.2.18 (#3347) 2026-06-24 21:50:27 +02:00
Quentin McGaw 52a41cb891 hotfix(command): fix streamLines behavior
- Fix #3365
2026-06-14 14:43:50 +00:00
Quentin McGaw 6c76273ef6 hotfix(socks5): bump UDP association packet queue channel capacity from 2 to 64
- Fix #3368
2026-06-14 14:24:17 +00:00
Quentin McGaw 366062dc12 chore(socks5): add server integration test for UDP 2026-06-11 16:55:39 +00:00
Quentin McGaw 8abb05567c hotfix(command): fix unit test 2026-06-11 14:06:26 +00:00
Quentin McGaw a53a0267e4 hotfix(socks5): support domain name udp association 2026-06-11 13:50:50 +00:00
Quentin McGaw 4e986c8af7 chore(socks5): fix lint errors on integration test 2026-06-11 13:37:58 +00:00
Quentin McGaw 6d84462f00 feat(socks5): UDP proxying (#3353) 2026-06-11 15:32:38 +02:00
Quentin McGaw acab89b91a fix(command): wait for all stdout and stderr streams to complete correctly 2026-06-11 13:30:59 +00:00
Quentin McGaw 48c1f2bf6a chore(lint): run linter on integration tests 2026-06-11 13:29:57 +00:00
Quentin McGaw c599e7fd2c chore(ci): disabe workflow concurrency by workflow-[pr|ref] 2026-06-05 15:50:01 +00:00
Quentin McGaw ff6e45fae0 chore(ci): disable PIA end to end testing due to expired credentials 2026-06-04 16:52:53 +00:00
89 changed files with 3148 additions and 593 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
FROM ghcr.io/qdm12/godevcontainer:v0.21-alpine FROM ghcr.io/qdm12/godevcontainer:v0.22-alpine
RUN apk add wireguard-tools htop openssl tcpdump iptables RUN apk add wireguard-tools htop openssl tcpdump iptables
RUN apk add nodejs npm && npm install markdownlint-cli2 --global && apk del npm RUN apk add nodejs npm && npm install markdownlint-cli2 --global && apk del npm
+14 -6
View File
@@ -28,6 +28,10 @@ on:
- go.mod - go.mod
- go.sum - go.sum
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
verify: verify:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -63,6 +67,10 @@ jobs:
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \ -v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
test-container test-container
- name: Run integration tests in test container
run: |
docker run --rm --entrypoint go test-container test -tags=integration ./internal/restrictednet
- name: Verify dev cross platform compatibility - name: Verify dev cross platform compatibility
run: docker build --target xcompile . run: docker build --target xcompile .
@@ -129,12 +137,12 @@ jobs:
secrets.PROTONVPN_OPENVPN_PASSWORD }}" | ./ci/runner secrets.PROTONVPN_OPENVPN_PASSWORD }}" | ./ci/runner
protonvpn-openvpn-port-forwarding protonvpn-openvpn-port-forwarding
- name: # - name:
Run Gluetun container with Private Internet Access OpenVPN and port # Run Gluetun container with Private Internet Access OpenVPN and port
forwarding configuration # forwarding configuration
run: echo -e "${{ secrets.PRIVATEINTERNETACCESS_OPENVPN_USER }}\n${{ # run: echo -e "${{ secrets.PRIVATEINTERNETACCESS_OPENVPN_USER }}\n${{
secrets.PRIVATEINTERNETACCESS_OPENVPN_PASSWORD }}" | ./ci/runner # secrets.PRIVATEINTERNETACCESS_OPENVPN_PASSWORD }}" | ./ci/runner
private-internet-access-openvpn-port-forwarding # private-internet-access-openvpn-port-forwarding
- name: Run Gluetun container with AirVPN Wireguard configuration - name: Run Gluetun container with AirVPN Wireguard configuration
run: echo -e "${{ secrets.AIRVPN_WIREGUARD_PRIVATE_KEY }}\n${{ run: echo -e "${{ secrets.AIRVPN_WIREGUARD_PRIVATE_KEY }}\n${{
+4
View File
@@ -11,6 +11,10 @@ on:
- "**.md" - "**.md"
- .github/workflows/markdown.yml - .github/workflows/markdown.yml
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
markdown: markdown:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+4
View File
@@ -12,6 +12,10 @@ formatters:
- builtin$ - builtin$
- examples$ - examples$
run:
build-tags:
- integration
linters: linters:
settings: settings:
misspell: misspell:
+1 -1
View File
@@ -3,7 +3,7 @@
// to develop this project. // to develop this project.
"files.eol": "\n", "files.eol": "\n",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"go.buildTags": "linux", "go.buildTags": "linux,integration",
"go.toolsEnvVars": { "go.toolsEnvVars": {
"CGO_ENABLED": "0" "CGO_ENABLED": "0"
}, },
+5
View File
@@ -50,6 +50,7 @@ Guidance for coding agents working in this repository.
- Prefer splitting a code line only when it triggers the `lll` linter, do not split a command or arguments list for each element - Prefer splitting a code line only when it triggers the `lll` linter, do not split a command or arguments list for each element
- Use `netip` types instead of `net` types whenever possible - Use `netip` types instead of `net` types whenever possible
- Use constants instead of variables whenever possible, especially function-local inline constants. - Use constants instead of variables whenever possible, especially function-local inline constants.
- Prefer using pure functions over methods when possible. Especially if the method does not need any fields from the receiving struct, it should be a pure function.
- Do not use `time.Sleep`, prefer using a `time.Timer` with a `select` statement also listening on a context cancelation - Do not use `time.Sleep`, prefer using a `time.Timer` with a `select` statement also listening on a context cancelation
- `panic`: - `panic`:
- should only be used when a programming error is encountered and you should NOT return errors for programming errors (such as passing nil objects) - should only be used when a programming error is encountered and you should NOT return errors for programming errors (such as passing nil objects)
@@ -115,6 +116,7 @@ Mocking works with the `go.uber.org/mock` library, and the `mockgen` tool.
- **Never** use `.AnyTimes()` on mocks. Always define the number of times a certain mock call should be called, with `.Times(3)` for example. - **Never** use `.AnyTimes()` on mocks. Always define the number of times a certain mock call should be called, with `.Times(3)` for example.
- **Always** set the `.Return(...)` on the mock if the function returns something. - **Always** set the `.Return(...)` on the mock if the function returns something.
- Avoid using **mock helpers** functions, prefer a bit of repetition than tight coupling and dependency - Avoid using **mock helpers** functions, prefer a bit of repetition than tight coupling and dependency
- Always define the gomock controller `ctrl` in the subtest and not in the parent test, or a subtest mock failing will crash all the other subtests.
### main.go ### main.go
@@ -127,6 +129,7 @@ The Go formatter used is gofumpt.
### Errors ### Errors
- Always prefer wrapping errors with some context with `fmt.Errorf("doing this: %w", err)` - Always prefer wrapping errors with some context with `fmt.Errorf("doing this: %w", err)`
- Use `errors.New("error message")` when creating a 'bottom' constant string error without additional context, instead of `fmt.Errorf`
- In rare cases, you can just use `return err` notably: - In rare cases, you can just use `return err` notably:
- If the function is called **recursively**, since we don't wrap the wrapping multiple times for each recursion - If the function is called **recursively**, since we don't wrap the wrapping multiple times for each recursion
- If the current function only statement is the call to another function, for example: - If the current function only statement is the call to another function, for example:
@@ -179,6 +182,8 @@ The Go formatter used is gofumpt.
- Do not use `http.DefaultClient`, use a custom `*http.Client` with a fixed timeout and share with dependency injections. - Do not use `http.DefaultClient`, use a custom `*http.Client` with a fixed timeout and share with dependency injections.
- Do not check for injected dependencies being `nil`, prefer to just panic on a nil pointer. By default it's fine to panic if a developer injects a dependency `nil`. `nil` does not mean use a default. - Do not check for injected dependencies being `nil`, prefer to just panic on a nil pointer. By default it's fine to panic if a developer injects a dependency `nil`. `nil` does not mean use a default.
- Prefer using a `switch { case ...}` statement over multiple consecutive `if` statements to have shorter code.
- Prefer using `[...]T` instead of `[]T` when the length is fixed and known at compile time, to avoid unnecessary allocations.
## Validation checklist ## Validation checklist
+2 -3
View File
@@ -3,7 +3,7 @@ ARG GO_ALPINE_VERSION=3.23
ARG GO_VERSION=1.25 ARG GO_VERSION=1.25
ARG XCPUTRANSLATE_VERSION=v0.9.0 ARG XCPUTRANSLATE_VERSION=v0.9.0
ARG GOLANGCI_LINT_VERSION=v2.4.0 ARG GOLANGCI_LINT_VERSION=v2.4.0
ARG MOCKGEN_VERSION=v1.6.0 ARG MOCKGEN_VERSION=v0.6.0
ARG BUILDPLATFORM=linux/amd64 ARG BUILDPLATFORM=linux/amd64
FROM --platform=${BUILDPLATFORM} ghcr.io/qdm12/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate FROM --platform=${BUILDPLATFORM} ghcr.io/qdm12/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
@@ -218,7 +218,6 @@ ENV VPN_SERVICE_PROVIDER=pia \
DNS_CACHING=on \ DNS_CACHING=on \
DNS_UPSTREAM_IPV6=off \ DNS_UPSTREAM_IPV6=off \
BLOCK_MALICIOUS=on \ BLOCK_MALICIOUS=on \
BLOCK_SURVEILLANCE=off \
BLOCK_ADS=off \ BLOCK_ADS=off \
DNS_UNBLOCK_HOSTNAMES= \ DNS_UNBLOCK_HOSTNAMES= \
DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \ DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \
@@ -276,7 +275,7 @@ ENV VPN_SERVICE_PROVIDER=pia \
PUID=1000 \ PUID=1000 \
PGID=1000 PGID=1000
ENTRYPOINT ["/gluetun-entrypoint"] ENTRYPOINT ["/gluetun-entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp 1080/tcp EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp 1080/tcp 1080/udp
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=3 CMD /gluetun-entrypoint healthcheck HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=3 CMD /gluetun-entrypoint healthcheck
ARG TARGETPLATFORM ARG TARGETPLATFORM
RUN apk add --no-cache --update -l wget && \ RUN apk add --no-cache --update -l wget && \
+2 -2
View File
@@ -69,11 +69,11 @@ Lightweight swiss-army-knife-like VPN client to multiple VPN service providers
- More in progress, see [#134](https://github.com/passteque/gluetun/issues/134) - More in progress, see [#134](https://github.com/passteque/gluetun/issues/134)
- Supports AmneziaWG only with the custom provider for now - Supports AmneziaWG only with the custom provider for now
- DNS over TLS baked in with service provider(s) of your choice - DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours - DNS fine blocking of malicious/ads hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp` - Choose the vpn network protocol, `udp` or `tcp`
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices - Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
- Built in Shadowsocks proxy server (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP) - Built in Shadowsocks proxy server (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
- Built in Socks5 proxy server (tunnels TCP) - partial credits to @angelakis and @adjscent - Built in Socks5 proxy server (tunnels TCP+UDP) - partial credits to @angelakis and @adjscent
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP) - Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
- [Connect other containers to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-container-to-gluetun.md) - [Connect other containers to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-container-to-gluetun.md)
- [Connect LAN devices to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-lan-device-to-gluetun.md) - [Connect LAN devices to it](https://github.com/qdm12/gluetun-wiki/blob/main/setup/connect-a-lan-device-to-gluetun.md)
+15 -15
View File
@@ -4,16 +4,16 @@ go 1.25.0
require ( require (
github.com/ProtonMail/go-srp v0.0.7 github.com/ProtonMail/go-srp v0.0.7
github.com/amnezia-vpn/amneziawg-go v0.2.16 github.com/amnezia-vpn/amneziawg-go v0.2.18
github.com/breml/rootcerts v0.3.5 github.com/breml/rootcerts v0.3.4
github.com/fatih/color v1.19.0 github.com/fatih/color v1.18.0
github.com/golang/mock v1.6.0
github.com/jsimonetti/rtnetlink v1.4.2 github.com/jsimonetti/rtnetlink v1.4.2
github.com/klauspost/compress v1.18.6 github.com/klauspost/compress v1.18.4
github.com/klauspost/pgzip v1.2.6 github.com/klauspost/pgzip v1.2.6
github.com/mdlayher/genetlink v1.3.2 github.com/mdlayher/genetlink v1.4.0
github.com/mdlayher/netlink v1.9.0 github.com/mdlayher/netlink v1.9.0
github.com/pelletier/go-toml/v2 v2.3.1 github.com/miekg/dns v1.1.62
github.com/pelletier/go-toml/v2 v2.2.4
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a
github.com/qdm12/gluetun-servers v0.1.0 github.com/qdm12/gluetun-servers v0.1.0
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978 github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978
@@ -27,12 +27,13 @@ require (
github.com/ti-mo/netfilter v0.5.3 github.com/ti-mo/netfilter v0.5.3
github.com/ulikunitz/xz v0.5.15 github.com/ulikunitz/xz v0.5.15
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
go.uber.org/mock v0.6.0
golang.org/x/net v0.55.0 golang.org/x/net v0.55.0
golang.org/x/sys v0.45.0 golang.org/x/sys v0.45.0
golang.org/x/text v0.37.0 golang.org/x/text v0.38.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
gopkg.in/ini.v1 v1.67.2 gopkg.in/ini.v1 v1.67.1
) )
require ( require (
@@ -44,10 +45,9 @@ require (
github.com/cronokirby/saferith v0.33.0 // indirect github.com/cronokirby/saferith v0.33.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/socket v0.6.0 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -58,9 +58,9 @@ require (
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
golang.org/x/crypto v0.51.0 // indirect golang.org/x/crypto v0.51.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.35.0 // indirect golang.org/x/mod v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect golang.org/x/sync v0.21.0 // indirect
golang.org/x/tools v0.44.0 // indirect golang.org/x/tools v0.45.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
+30 -36
View File
@@ -6,12 +6,12 @@ github.com/ProtonMail/go-crypto v1.3.0-proton h1:tAQKQRZX/73VmzK6yHSCaRUOvS/3OYS
github.com/ProtonMail/go-crypto v1.3.0-proton/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/ProtonMail/go-crypto v1.3.0-proton/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI= github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk= github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
github.com/amnezia-vpn/amneziawg-go v0.2.16 h1:XY6HOq/xtqH8ZXMncRWkjFs85EKdN10NLNnw23kTpE0= github.com/amnezia-vpn/amneziawg-go v0.2.18 h1:pUn7/P8qdGmHd6JmE3bCQXPblZs3vruWR98nLODQLJg=
github.com/amnezia-vpn/amneziawg-go v0.2.16/go.mod h1:nRkPpIzjCxMW8pZKXTRkpqAQVlmFJdVOGkeQSC7wbms= github.com/amnezia-vpn/amneziawg-go v0.2.18/go.mod h1:aMgOk9MuX0xI7b5TKAYp8pLM54RlXcOPzDvYw3YEO5A=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/breml/rootcerts v0.3.5 h1:oi7YiZ25HH52+mrKyjrMkcAFfnRDUf6HO8aUDr7RlJI= github.com/breml/rootcerts v0.3.4 h1:9i7WNl/ctd9OEAOaTfLy//Wrlfxq/tRQ7v4okYFN9Ys=
github.com/breml/rootcerts v0.3.5/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw= github.com/breml/rootcerts v0.3.4/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -25,8 +25,8 @@ github.com/cronokirby/saferith v0.33.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
@@ -35,32 +35,33 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90= github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90=
github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM= github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.4.0 h1:f/Xs7Y2T+GyX9b3dbiUhnLE9InGs5F9RxJ2JwBMl71o=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/genetlink v1.4.0/go.mod h1:d1hrKr8fwZU2JkcAtQUAzeTrI7nbgQSl+5k1cC0biSA=
github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco= github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco=
github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg= github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -110,12 +111,12 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
@@ -123,35 +124,31 @@ golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
@@ -165,20 +162,17 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
@@ -190,8 +184,8 @@ google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss= gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+29 -23
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=log_mock_test.go -package amneziawg . Logger
//
// Package amneziawg is a generated GoMock package. // Package amneziawg is a generated GoMock package.
package amneziawg package amneziawg
@@ -7,13 +12,14 @@ package amneziawg
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,71 +40,71 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Debug mocks base method. // Debug mocks base method.
func (m *MockLogger) Debug(arg0 string) { func (m *MockLogger) Debug(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Debug", arg0) m.ctrl.Call(m, "Debug", s)
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), s)
} }
// Debugf mocks base method. // Debugf mocks base method.
func (m *MockLogger) Debugf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Debugf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Debugf", varargs...) m.ctrl.Call(m, "Debugf", varargs...)
} }
// Debugf indicates an expected call of Debugf. // Debugf indicates an expected call of Debugf.
func (mr *MockLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debugf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...)
} }
// Error mocks base method. // Error mocks base method.
func (m *MockLogger) Error(arg0 string) { func (m *MockLogger) Error(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Error", arg0) m.ctrl.Call(m, "Error", s)
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), s)
} }
// Errorf mocks base method. // Errorf mocks base method.
func (m *MockLogger) Errorf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Errorf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Errorf", varargs...) m.ctrl.Call(m, "Errorf", varargs...)
} }
// Errorf indicates an expected call of Errorf. // Errorf indicates an expected call of Errorf.
func (mr *MockLoggerMockRecorder) Errorf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Errorf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...)
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", s)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), s)
} }
+47 -41
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: NetLinker) // Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: NetLinker)
//
// Generated by this command:
//
// mockgen -destination=netlinker_mock_test.go -package amneziawg . NetLinker
//
// Package amneziawg is a generated GoMock package. // Package amneziawg is a generated GoMock package.
package amneziawg package amneziawg
@@ -8,14 +13,15 @@ import (
netip "net/netip" netip "net/netip"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
netlink "github.com/qdm12/gluetun/internal/netlink" netlink "github.com/qdm12/gluetun/internal/netlink"
gomock "go.uber.org/mock/gomock"
) )
// MockNetLinker is a mock of NetLinker interface. // MockNetLinker is a mock of NetLinker interface.
type MockNetLinker struct { type MockNetLinker struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockNetLinkerMockRecorder recorder *MockNetLinkerMockRecorder
isgomock struct{}
} }
// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker. // MockNetLinkerMockRecorder is the mock recorder for MockNetLinker.
@@ -36,17 +42,17 @@ func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder {
} }
// AddrReplace mocks base method. // AddrReplace mocks base method.
func (m *MockNetLinker) AddrReplace(arg0 uint32, arg1 netip.Prefix) error { func (m *MockNetLinker) AddrReplace(linkIndex uint32, addr netip.Prefix) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrReplace", arg0, arg1) ret := m.ctrl.Call(m, "AddrReplace", linkIndex, addr)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// AddrReplace indicates an expected call of AddrReplace. // AddrReplace indicates an expected call of AddrReplace.
func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) AddrReplace(linkIndex, addr any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), linkIndex, addr)
} }
// IsWireguardSupported mocks base method. // IsWireguardSupported mocks base method.
@@ -65,47 +71,47 @@ func (mr *MockNetLinkerMockRecorder) IsWireguardSupported() *gomock.Call {
} }
// LinkAdd mocks base method. // LinkAdd mocks base method.
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) (uint32, error) { func (m *MockNetLinker) LinkAdd(link netlink.Link) (uint32, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkAdd", arg0) ret := m.ctrl.Call(m, "LinkAdd", link)
ret0, _ := ret[0].(uint32) ret0, _ := ret[0].(uint32)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkAdd indicates an expected call of LinkAdd. // LinkAdd indicates an expected call of LinkAdd.
func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkAdd(link any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), link)
} }
// LinkByName mocks base method. // LinkByName mocks base method.
func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) { func (m *MockNetLinker) LinkByName(name string) (netlink.Link, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByName", arg0) ret := m.ctrl.Call(m, "LinkByName", name)
ret0, _ := ret[0].(netlink.Link) ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkByName indicates an expected call of LinkByName. // LinkByName indicates an expected call of LinkByName.
func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkByName(name any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), name)
} }
// LinkDel mocks base method. // LinkDel mocks base method.
func (m *MockNetLinker) LinkDel(arg0 uint32) error { func (m *MockNetLinker) LinkDel(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkDel", arg0) ret := m.ctrl.Call(m, "LinkDel", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkDel indicates an expected call of LinkDel. // LinkDel indicates an expected call of LinkDel.
func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkDel(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), linkIndex)
} }
// LinkList mocks base method. // LinkList mocks base method.
@@ -124,86 +130,86 @@ func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call {
} }
// LinkSetDown mocks base method. // LinkSetDown mocks base method.
func (m *MockNetLinker) LinkSetDown(arg0 uint32) error { func (m *MockNetLinker) LinkSetDown(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetDown", arg0) ret := m.ctrl.Call(m, "LinkSetDown", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetDown indicates an expected call of LinkSetDown. // LinkSetDown indicates an expected call of LinkSetDown.
func (mr *MockNetLinkerMockRecorder) LinkSetDown(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetDown(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), linkIndex)
} }
// LinkSetUp mocks base method. // LinkSetUp mocks base method.
func (m *MockNetLinker) LinkSetUp(arg0 uint32) error { func (m *MockNetLinker) LinkSetUp(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetUp", arg0) ret := m.ctrl.Call(m, "LinkSetUp", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetUp indicates an expected call of LinkSetUp. // LinkSetUp indicates an expected call of LinkSetUp.
func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetUp(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), linkIndex)
} }
// RouteAdd mocks base method. // RouteAdd mocks base method.
func (m *MockNetLinker) RouteAdd(arg0 netlink.Route) error { func (m *MockNetLinker) RouteAdd(route netlink.Route) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteAdd", arg0) ret := m.ctrl.Call(m, "RouteAdd", route)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RouteAdd indicates an expected call of RouteAdd. // RouteAdd indicates an expected call of RouteAdd.
func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteAdd(route any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), route)
} }
// RouteList mocks base method. // RouteList mocks base method.
func (m *MockNetLinker) RouteList(arg0 byte) ([]netlink.Route, error) { func (m *MockNetLinker) RouteList(family uint8) ([]netlink.Route, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteList", arg0) ret := m.ctrl.Call(m, "RouteList", family)
ret0, _ := ret[0].([]netlink.Route) ret0, _ := ret[0].([]netlink.Route)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// RouteList indicates an expected call of RouteList. // RouteList indicates an expected call of RouteList.
func (mr *MockNetLinkerMockRecorder) RouteList(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteList(family any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), family)
} }
// RuleAdd mocks base method. // RuleAdd mocks base method.
func (m *MockNetLinker) RuleAdd(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleAdd(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleAdd", arg0) ret := m.ctrl.Call(m, "RuleAdd", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleAdd indicates an expected call of RuleAdd. // RuleAdd indicates an expected call of RuleAdd.
func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleAdd(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), rule)
} }
// RuleDel mocks base method. // RuleDel mocks base method.
func (m *MockNetLinker) RuleDel(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleDel(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleDel", arg0) ret := m.ctrl.Call(m, "RuleDel", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleDel indicates an expected call of RuleDel. // RuleDel indicates an expected call of RuleDel.
func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleDel(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), rule)
} }
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_Cleanups(t *testing.T) { func Test_Cleanups(t *testing.T) {
+9 -3
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/cleanup (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/cleanup (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=cleanup . Logger
//
// Package cleanup is a generated GoMock package. // Package cleanup is a generated GoMock package.
package cleanup package cleanup
@@ -7,13 +12,14 @@ package cleanup
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -40,7 +46,7 @@ func (m *MockLogger) Debug(arg0 string) {
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0)
} }
@@ -52,7 +58,7 @@ func (m *MockLogger) Error(arg0 string) {
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0)
} }
+7 -1
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: interfaces_local.go // Source: interfaces_local.go
//
// Generated by this command:
//
// mockgen -destination=mocks_local_test.go -package=command -source=interfaces_local.go
//
// Package command is a generated GoMock package. // Package command is a generated GoMock package.
package command package command
@@ -8,13 +13,14 @@ import (
io "io" io "io"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockexecCmd is a mock of execCmd interface. // MockexecCmd is a mock of execCmd interface.
type MockexecCmd struct { type MockexecCmd struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockexecCmdMockRecorder recorder *MockexecCmdMockRecorder
isgomock struct{}
} }
// MockexecCmdMockRecorder is the mock recorder for MockexecCmd. // MockexecCmdMockRecorder is the mock recorder for MockexecCmd.
+1 -1
View File
@@ -4,9 +4,9 @@ import (
"errors" "errors"
"testing" "testing"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
) )
func Test_run(t *testing.T) { func Test_run(t *testing.T) {
+10 -4
View File
@@ -9,8 +9,9 @@ import (
) )
// Start launches a command and streams stdout and stderr to channels. // Start launches a command and streams stdout and stderr to channels.
// All the channels returned are ready only and won't be closed // stdoutLines and stderrLines channels will be closed when there is no more
// if the command fails later. // output to read, in order for the caller to catch all lines even after the
// command has finished. The waitError channel returned will never be closed.
func (c *Cmder) Start(cmd *exec.Cmd) ( func (c *Cmder) Start(cmd *exec.Cmd) (
stdoutLines, stderrLines <-chan string, stdoutLines, stderrLines <-chan string,
waitError <-chan error, startErr error, waitError <-chan error, startErr error,
@@ -38,6 +39,7 @@ func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
if err != nil { if err != nil {
_ = stdout.Close() _ = stdout.Close()
<-stdoutDone <-stdoutDone
close(stdoutLinesCh)
return nil, nil, nil, err return nil, nil, nil, err
} }
go streamToChannel(stderrReady, stderrDone, stderr, stderrLinesCh) go streamToChannel(stderrReady, stderrDone, stderr, stderrLinesCh)
@@ -45,9 +47,11 @@ func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
_ = stdout.Close() _ = stdout.Close()
_ = stderr.Close()
<-stdoutDone <-stdoutDone
close(stdoutLinesCh)
_ = stderr.Close()
<-stderrDone <-stderrDone
close(stderrLinesCh)
return nil, nil, nil, err return nil, nil, nil, err
} }
@@ -55,8 +59,10 @@ func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
<-stdoutDone <-stdoutDone
<-stderrDone close(stdoutLinesCh)
_ = stdout.Close() _ = stdout.Close()
<-stderrDone
close(stderrLinesCh)
_ = stderr.Close() _ = stderr.Close()
waitErrorCh <- err waitErrorCh <- err
}() }()
+43 -25
View File
@@ -7,9 +7,9 @@ import (
"strings" "strings"
"testing" "testing"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
) )
func linesToReadCloser(lines []string) io.ReadCloser { func linesToReadCloser(lines []string) io.ReadCloser {
@@ -89,30 +89,48 @@ func Test_start(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
var stdoutIndex, stderrIndex int collectAndCheckChannels(t, stdoutLines, stderrLines, waitError,
testCase.stdout, testCase.stderr, testCase.waitErr)
done := false
for !done {
select {
case line := <-stdoutLines:
assert.Equal(t, testCase.stdout[stdoutIndex], line)
stdoutIndex++
case line := <-stderrLines:
assert.Equal(t, testCase.stderr[stderrIndex], line)
stderrIndex++
case err := <-waitError:
if testCase.waitErr != nil {
require.Error(t, err)
assert.Equal(t, testCase.waitErr.Error(), err.Error())
} else {
assert.NoError(t, err)
}
done = true
}
}
assert.Equal(t, len(testCase.stdout), stdoutIndex)
assert.Equal(t, len(testCase.stderr), stderrIndex)
}) })
} }
} }
func collectAndCheckChannels(t *testing.T, stdoutLines, stderrLines <-chan string,
waitError <-chan error, expectedStdout, expectedStderr []string, expectedWaitErr error,
) {
t.Helper()
stdoutIndex := 0
stderrIndex := 0
done := false
for !done {
select {
case line, ok := <-stdoutLines:
if !ok {
stdoutLines = nil
continue
}
assert.Equal(t, expectedStdout[stdoutIndex], line)
stdoutIndex++
case line, ok := <-stderrLines:
if !ok {
stderrLines = nil
continue
}
assert.Equal(t, expectedStderr[stderrIndex], line)
stderrIndex++
case err := <-waitError:
if expectedWaitErr != nil {
require.Error(t, err)
assert.Equal(t, expectedWaitErr.Error(), err.Error())
} else {
assert.NoError(t, err)
}
done = true
}
}
assert.Equal(t, len(expectedStdout), stdoutIndex)
assert.Equal(t, len(expectedStderr), stderrIndex)
}
+21 -13
View File
@@ -18,31 +18,39 @@ func (c *Cmder) RunAndLog(ctx context.Context, command string, logger Logger) (e
return err return err
} }
streamCtx, streamCancel := context.WithCancel(context.Background())
streamDone := make(chan struct{}) streamDone := make(chan struct{})
go streamLines(streamCtx, streamDone, logger, stdout, stderr) go streamLines(streamDone, logger, stdout, stderr)
err = <-waitError err = <-waitError
streamCancel()
<-streamDone <-streamDone
return err return err
} }
func streamLines(ctx context.Context, done chan<- struct{}, func streamLines(done chan<- struct{}, logger Logger,
logger Logger, stdout, stderr <-chan string, stdout, stderr <-chan string,
) { ) {
defer close(done) defer close(done)
var line string
for { for {
select { select {
case <-ctx.Done(): case line, ok := <-stdout:
return if ok {
case line = <-stdout: logger.Info(line)
logger.Info(line) break
case line = <-stderr: }
logger.Error(line) if stderr == nil {
return
}
stdout = nil
case line, ok := <-stderr:
if ok {
logger.Error(line)
break
}
if stdout == nil {
return
}
stderr = nil
} }
} }
} }
@@ -16,6 +16,7 @@ func readObsolete(r *reader.Reader) (warnings []string) {
"HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete", "HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete",
"DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because you should use the built-in server which now " + "DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because you should use the built-in server which now " +
"forwards local names to private DNS resolvers found in /etc/resolv.conf at container start", "forwards local names to private DNS resolvers found in /etc/resolv.conf at container start",
"BLOCK_SURVEILLANCE": "BLOCK_SURVEILLANCE is obsolete because its DNS block lists are not longer maintained",
} }
sortedKeys := slices.Collect(maps.Keys(keyToMessage)) sortedKeys := slices.Collect(maps.Keys(keyToMessage))
slices.Sort(sortedKeys) slices.Sort(sortedKeys)
@@ -16,7 +16,6 @@ import (
type DNSBlacklist struct { type DNSBlacklist struct {
BlockMalicious *bool BlockMalicious *bool
BlockAds *bool BlockAds *bool
BlockSurveillance *bool
AllowedHosts []string AllowedHosts []string
AddBlockedHosts []string AddBlockedHosts []string
AddBlockedIPs []netip.Addr AddBlockedIPs []netip.Addr
@@ -31,7 +30,6 @@ type DNSBlacklist struct {
func (b *DNSBlacklist) setDefaults() { func (b *DNSBlacklist) setDefaults() {
b.BlockMalicious = gosettings.DefaultPointer(b.BlockMalicious, true) b.BlockMalicious = gosettings.DefaultPointer(b.BlockMalicious, true)
b.BlockAds = gosettings.DefaultPointer(b.BlockAds, false) b.BlockAds = gosettings.DefaultPointer(b.BlockAds, false)
b.BlockSurveillance = gosettings.DefaultPointer(b.BlockSurveillance, true)
} }
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll
@@ -65,7 +63,6 @@ func (b DNSBlacklist) copy() (copied DNSBlacklist) {
return DNSBlacklist{ return DNSBlacklist{
BlockMalicious: gosettings.CopyPointer(b.BlockMalicious), BlockMalicious: gosettings.CopyPointer(b.BlockMalicious),
BlockAds: gosettings.CopyPointer(b.BlockAds), BlockAds: gosettings.CopyPointer(b.BlockAds),
BlockSurveillance: gosettings.CopyPointer(b.BlockSurveillance),
AllowedHosts: gosettings.CopySlice(b.AllowedHosts), AllowedHosts: gosettings.CopySlice(b.AllowedHosts),
AddBlockedHosts: gosettings.CopySlice(b.AddBlockedHosts), AddBlockedHosts: gosettings.CopySlice(b.AddBlockedHosts),
AddBlockedIPs: gosettings.CopySlice(b.AddBlockedIPs), AddBlockedIPs: gosettings.CopySlice(b.AddBlockedIPs),
@@ -77,7 +74,6 @@ func (b DNSBlacklist) copy() (copied DNSBlacklist) {
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) { func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious) b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds) b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds)
b.BlockSurveillance = gosettings.OverrideWithPointer(b.BlockSurveillance, other.BlockSurveillance)
b.AllowedHosts = gosettings.OverrideWithSlice(b.AllowedHosts, other.AllowedHosts) b.AllowedHosts = gosettings.OverrideWithSlice(b.AllowedHosts, other.AllowedHosts)
b.AddBlockedHosts = gosettings.OverrideWithSlice(b.AddBlockedHosts, other.AddBlockedHosts) b.AddBlockedHosts = gosettings.OverrideWithSlice(b.AddBlockedHosts, other.AddBlockedHosts)
b.AddBlockedIPs = gosettings.OverrideWithSlice(b.AddBlockedIPs, other.AddBlockedIPs) b.AddBlockedIPs = gosettings.OverrideWithSlice(b.AddBlockedIPs, other.AddBlockedIPs)
@@ -93,7 +89,6 @@ func (b DNSBlacklist) ToBlockBuilderSettings(client *http.Client) (
Client: client, Client: client,
BlockMalicious: b.BlockMalicious, BlockMalicious: b.BlockMalicious,
BlockAds: b.BlockAds, BlockAds: b.BlockAds,
BlockSurveillance: b.BlockSurveillance,
AllowedHosts: b.AllowedHosts, AllowedHosts: b.AllowedHosts,
AddBlockedHosts: b.AddBlockedHosts, AddBlockedHosts: b.AddBlockedHosts,
AddBlockedIPs: b.AddBlockedIPs, AddBlockedIPs: b.AddBlockedIPs,
@@ -110,7 +105,6 @@ func (b DNSBlacklist) toLinesNode() (node *gotree.Node) {
node.Appendf("Block malicious: %s", gosettings.BoolToYesNo(b.BlockMalicious)) node.Appendf("Block malicious: %s", gosettings.BoolToYesNo(b.BlockMalicious))
node.Appendf("Block ads: %s", gosettings.BoolToYesNo(b.BlockAds)) node.Appendf("Block ads: %s", gosettings.BoolToYesNo(b.BlockAds))
node.Appendf("Block surveillance: %s", gosettings.BoolToYesNo(b.BlockSurveillance))
if len(b.AllowedHosts) > 0 { if len(b.AllowedHosts) > 0 {
allowedHostsNode := node.Append("Allowed hosts:") allowedHostsNode := node.Append("Allowed hosts:")
@@ -156,12 +150,6 @@ func (b *DNSBlacklist) read(r *reader.Reader) (err error) {
return err return err
} }
b.BlockSurveillance, err = r.BoolPtr("BLOCK_SURVEILLANCE",
reader.RetroKeys("BLOCK_NSA"))
if err != nil {
return err
}
b.BlockAds, err = r.BoolPtr("BLOCK_ADS") b.BlockAds, err = r.BoolPtr("BLOCK_ADS")
if err != nil { if err != nil {
return err return err
@@ -1,6 +1,6 @@
package settings package settings
import gomock "github.com/golang/mock/gomock" import gomock "go.uber.org/mock/gomock"
type sourceKeyValue struct { type sourceKeyValue struct {
key string key string
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gosettings/reader (interfaces: Source) // Source: github.com/qdm12/gosettings/reader (interfaces: Source)
//
// Generated by this command:
//
// mockgen -destination=mocks_reader_test.go -package=settings github.com/qdm12/gosettings/reader Source
//
// Package settings is a generated GoMock package. // Package settings is a generated GoMock package.
package settings package settings
@@ -7,13 +12,14 @@ package settings
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockSource is a mock of Source interface. // MockSource is a mock of Source interface.
type MockSource struct { type MockSource struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockSourceMockRecorder recorder *MockSourceMockRecorder
isgomock struct{}
} }
// MockSourceMockRecorder is the mock recorder for MockSource. // MockSourceMockRecorder is the mock recorder for MockSource.
@@ -34,32 +40,32 @@ func (m *MockSource) EXPECT() *MockSourceMockRecorder {
} }
// Get mocks base method. // Get mocks base method.
func (m *MockSource) Get(arg0 string) (string, bool) { func (m *MockSource) Get(key string) (string, bool) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", arg0) ret := m.ctrl.Call(m, "Get", key)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
ret1, _ := ret[1].(bool) ret1, _ := ret[1].(bool)
return ret0, ret1 return ret0, ret1
} }
// Get indicates an expected call of Get. // Get indicates an expected call of Get.
func (mr *MockSourceMockRecorder) Get(arg0 interface{}) *gomock.Call { func (mr *MockSourceMockRecorder) Get(key any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSource)(nil).Get), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSource)(nil).Get), key)
} }
// KeyTransform mocks base method. // KeyTransform mocks base method.
func (m *MockSource) KeyTransform(arg0 string) string { func (m *MockSource) KeyTransform(key string) string {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "KeyTransform", arg0) ret := m.ctrl.Call(m, "KeyTransform", key)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
return ret0 return ret0
} }
// KeyTransform indicates an expected call of KeyTransform. // KeyTransform indicates an expected call of KeyTransform.
func (mr *MockSourceMockRecorder) KeyTransform(arg0 interface{}) *gomock.Call { func (mr *MockSourceMockRecorder) KeyTransform(key any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyTransform", reflect.TypeOf((*MockSource)(nil).KeyTransform), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyTransform", reflect.TypeOf((*MockSource)(nil).KeyTransform), key)
} }
// String mocks base method. // String mocks base method.
+11 -5
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/configuration/settings (interfaces: Warner) // Source: github.com/qdm12/gluetun/internal/configuration/settings (interfaces: Warner)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=settings . Warner
//
// Package settings is a generated GoMock package. // Package settings is a generated GoMock package.
package settings package settings
@@ -7,13 +12,14 @@ package settings
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockWarner is a mock of Warner interface. // MockWarner is a mock of Warner interface.
type MockWarner struct { type MockWarner struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockWarnerMockRecorder recorder *MockWarnerMockRecorder
isgomock struct{}
} }
// MockWarnerMockRecorder is the mock recorder for MockWarner. // MockWarnerMockRecorder is the mock recorder for MockWarner.
@@ -34,13 +40,13 @@ func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockWarner) Warn(arg0 string) { func (m *MockWarner) Warn(message string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", message)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockWarnerMockRecorder) Warn(message any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), message)
} }
@@ -3,9 +3,9 @@ package settings
import ( import (
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gosettings/reader" "github.com/qdm12/gosettings/reader"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_PublicIP_read(t *testing.T) { func Test_PublicIP_read(t *testing.T) {
@@ -59,8 +59,7 @@ func Test_Settings_String(t *testing.T) {
| ├── Update period: every 24h0m0s | ├── Update period: every 24h0m0s
| └── DNS filtering settings: | └── DNS filtering settings:
| ├── Block malicious: yes | ├── Block malicious: yes
| ── Block ads: no | ── Block ads: no
| └── Block surveillance: yes
├── Firewall settings: ├── Firewall settings:
| ├── Enabled: yes | ├── Enabled: yes
| └── Iptables settings: | └── Iptables settings:
+2
View File
@@ -28,6 +28,8 @@ type firewallImpl interface { //nolint:interfacebloat
AcceptIpv6MulticastOutput(ctx context.Context, intf string) error AcceptIpv6MulticastOutput(ctx context.Context, intf string) error
AcceptOutput(ctx context.Context, protocol, intf string, AcceptOutput(ctx context.Context, protocol, intf string,
ip netip.Addr, port uint16, remove bool) error ip netip.Addr, port uint16, remove bool) error
AcceptOutputFromIPPortToIPPort(ctx context.Context, protocol, intf string,
source, destination netip.AddrPort, remove bool) error
AcceptOutputFromIPToSubnet(ctx context.Context, intf string, assignedIP netip.Addr, AcceptOutputFromIPToSubnet(ctx context.Context, intf string, assignedIP netip.Addr,
subnet netip.Prefix, remove bool) error subnet netip.Prefix, remove bool) error
AcceptOutputThroughInterface(ctx context.Context, intf string, remove bool) error AcceptOutputThroughInterface(ctx context.Context, intf string, remove bool) error
@@ -5,7 +5,7 @@ import (
"os/exec" "os/exec"
"regexp" "regexp"
"github.com/golang/mock/gomock" "go.uber.org/mock/gomock"
) )
var _ gomock.Matcher = (*cmdMatcher)(nil) var _ gomock.Matcher = (*cmdMatcher)(nil)
+1 -1
View File
@@ -5,8 +5,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_isDeleteMatchInstruction(t *testing.T) { func Test_isDeleteMatchInstruction(t *testing.T) {
+24
View File
@@ -2,6 +2,7 @@ package iptables
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net/netip" "net/netip"
@@ -177,6 +178,29 @@ func (c *Config) AcceptOutput(ctx context.Context,
return c.runIP6tablesInstruction(ctx, instruction) return c.runIP6tablesInstruction(ctx, instruction)
} }
func (c *Config) AcceptOutputFromIPPortToIPPort(ctx context.Context,
protocol, intf string, source, destination netip.AddrPort, remove bool,
) error {
if source.Addr().BitLen() != destination.Addr().BitLen() {
return errors.New("source and destination address families do not match")
}
interfaceFlag := "-o " + intf
if intf == "*" { // all interfaces
interfaceFlag = ""
}
instruction := fmt.Sprintf("%s OUTPUT %s -s %s -d %s -p %s -m %s --sport %d --dport %d -j ACCEPT",
appendOrDelete(remove), interfaceFlag, source.Addr(), destination.Addr(),
protocol, protocol, source.Port(), destination.Port())
if destination.Addr().Is4() {
return c.runIptablesInstruction(ctx, instruction)
} else if c.ip6Tables == "" {
return fmt.Errorf("accept output from %s to %s: %s", source, destination, needIP6Tables)
}
return c.runIP6tablesInstruction(ctx, instruction)
}
// AcceptOutputFromIPToSubnet accepts outgoing traffic from sourceIP to destinationSubnet // AcceptOutputFromIPToSubnet accepts outgoing traffic from sourceIP to destinationSubnet
// on the interface intf. If intf is empty, it is set to "*" which means all interfaces. // on the interface intf. If intf is empty, it is set to "*" which means all interfaces.
// If remove is true, the rule is removed instead of added. // If remove is true, the rule is removed instead of added.
+20 -13
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/firewall/iptables (interfaces: CmdRunner,Logger) // Source: github.com/qdm12/gluetun/internal/firewall/iptables (interfaces: CmdRunner,Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package iptables . CmdRunner,Logger
//
// Package iptables is a generated GoMock package. // Package iptables is a generated GoMock package.
package iptables package iptables
@@ -8,13 +13,14 @@ import (
exec "os/exec" exec "os/exec"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockCmdRunner is a mock of CmdRunner interface. // MockCmdRunner is a mock of CmdRunner interface.
type MockCmdRunner struct { type MockCmdRunner struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockCmdRunnerMockRecorder recorder *MockCmdRunnerMockRecorder
isgomock struct{}
} }
// MockCmdRunnerMockRecorder is the mock recorder for MockCmdRunner. // MockCmdRunnerMockRecorder is the mock recorder for MockCmdRunner.
@@ -35,24 +41,25 @@ func (m *MockCmdRunner) EXPECT() *MockCmdRunnerMockRecorder {
} }
// Run mocks base method. // Run mocks base method.
func (m *MockCmdRunner) Run(arg0 *exec.Cmd) (string, error) { func (m *MockCmdRunner) Run(cmd *exec.Cmd) (string, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Run", arg0) ret := m.ctrl.Call(m, "Run", cmd)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// Run indicates an expected call of Run. // Run indicates an expected call of Run.
func (mr *MockCmdRunnerMockRecorder) Run(arg0 interface{}) *gomock.Call { func (mr *MockCmdRunnerMockRecorder) Run(cmd any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockCmdRunner)(nil).Run), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockCmdRunner)(nil).Run), cmd)
} }
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -73,25 +80,25 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Debug mocks base method. // Debug mocks base method.
func (m *MockLogger) Debug(arg0 string) { func (m *MockLogger) Debug(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Debug", arg0) m.ctrl.Call(m, "Debug", s)
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), s)
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockLogger) Warn(arg0 string) { func (m *MockLogger) Warn(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", s)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warn(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), s)
} }
+1 -1
View File
@@ -5,8 +5,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func newAppendTestRuleMatcher(path string) *cmdMatcher { func newAppendTestRuleMatcher(path string) *cmdMatcher {
+7
View File
@@ -25,3 +25,10 @@ func (c *Config) AcceptOutput(ctx context.Context, protocol, intf string,
) error { ) error {
return c.impl.AcceptOutput(ctx, protocol, intf, ip, port, remove) return c.impl.AcceptOutput(ctx, protocol, intf, ip, port, remove)
} }
func (c *Config) AcceptOutputFromIPPortToIPPort(ctx context.Context,
protocol, intf string, source, destination netip.AddrPort, remove bool,
) error {
return c.impl.AcceptOutputFromIPPortToIPPort(ctx, protocol, intf,
source, destination, remove)
}
+1 -1
View File
@@ -3,7 +3,7 @@ package httpserver
import ( import (
"regexp" "regexp"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
var _ Logger = (*testLogger)(nil) var _ Logger = (*testLogger)(nil)
+19 -13
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/httpserver (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/httpserver (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=logger_mock_test.go -package httpserver . Logger
//
// Package httpserver is a generated GoMock package. // Package httpserver is a generated GoMock package.
package httpserver package httpserver
@@ -7,13 +12,14 @@ package httpserver
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,37 +40,37 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Error mocks base method. // Error mocks base method.
func (m *MockLogger) Error(arg0 string) { func (m *MockLogger) Error(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Error", arg0) m.ctrl.Call(m, "Error", msg)
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), msg)
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", msg)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), msg)
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockLogger) Warn(arg0 string) { func (m *MockLogger) Warn(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", msg)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warn(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), msg)
} }
+1 -1
View File
@@ -6,8 +6,8 @@ import (
"testing" "testing"
"time" "time"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
gomock "go.uber.org/mock/gomock"
) )
func Test_Server_Run_success(t *testing.T) { func Test_Server_Run_success(t *testing.T) {
+1 -1
View File
@@ -9,9 +9,9 @@ import (
"testing" "testing"
"time" "time"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
) )
func isIPv6LocallySupported() bool { func isIPv6LocallySupported() bool {
+11 -5
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/netlink (interfaces: Firewall) // Source: github.com/qdm12/gluetun/internal/netlink (interfaces: Firewall)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=netlink . Firewall
//
// Package netlink is a generated GoMock package. // Package netlink is a generated GoMock package.
package netlink package netlink
@@ -9,13 +14,14 @@ import (
netip "net/netip" netip "net/netip"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockFirewall is a mock of Firewall interface. // MockFirewall is a mock of Firewall interface.
type MockFirewall struct { type MockFirewall struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockFirewallMockRecorder recorder *MockFirewallMockRecorder
isgomock struct{}
} }
// MockFirewallMockRecorder is the mock recorder for MockFirewall. // MockFirewallMockRecorder is the mock recorder for MockFirewall.
@@ -36,15 +42,15 @@ func (m *MockFirewall) EXPECT() *MockFirewallMockRecorder {
} }
// AcceptOutput mocks base method. // AcceptOutput mocks base method.
func (m *MockFirewall) AcceptOutput(arg0 context.Context, arg1, arg2 string, arg3 netip.Addr, arg4 uint16, arg5 bool) error { func (m *MockFirewall) AcceptOutput(ctx context.Context, protocol, intf string, ip netip.Addr, port uint16, remove bool) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AcceptOutput", arg0, arg1, arg2, arg3, arg4, arg5) ret := m.ctrl.Call(m, "AcceptOutput", ctx, protocol, intf, ip, port, remove)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// AcceptOutput indicates an expected call of AcceptOutput. // AcceptOutput indicates an expected call of AcceptOutput.
func (mr *MockFirewallMockRecorder) AcceptOutput(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { func (mr *MockFirewallMockRecorder) AcceptOutput(ctx, protocol, intf, ip, port, remove any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptOutput", reflect.TypeOf((*MockFirewall)(nil).AcceptOutput), arg0, arg1, arg2, arg3, arg4, arg5) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptOutput", reflect.TypeOf((*MockFirewall)(nil).AcceptOutput), ctx, protocol, intf, ip, port, remove)
} }
+1 -4
View File
@@ -29,19 +29,16 @@ func (r *Runner) Run(ctx context.Context, errCh chan<- error, ready chan<- struc
return return
} }
streamCtx, streamCancel := context.WithCancel(context.Background())
streamDone := make(chan struct{}) streamDone := make(chan struct{})
go streamLines(streamCtx, streamDone, r.logger, go streamLines(streamDone, r.logger,
stdoutLines, stderrLines, ready) stdoutLines, stderrLines, ready)
select { select {
case <-ctx.Done(): case <-ctx.Done():
<-waitError <-waitError
streamCancel()
<-streamDone <-streamDone
errCh <- ctx.Err() errCh <- ctx.Err()
case err := <-waitError: case err := <-waitError:
streamCancel()
<-streamDone <-streamDone
errCh <- err errCh <- err
} }
+20 -9
View File
@@ -1,26 +1,37 @@
package openvpn package openvpn
import ( import (
"context"
"strings" "strings"
) )
func streamLines(ctx context.Context, done chan<- struct{}, func streamLines(done chan<- struct{},
logger Logger, stdout, stderr <-chan string, logger Logger, stdout, stderr <-chan string,
tunnelReady chan<- struct{}, tunnelReady chan<- struct{},
) { ) {
defer close(done) defer close(done)
var line string
for { for {
var line string
var ok bool
errLine := false errLine := false
select { select {
case <-ctx.Done(): case line, ok = <-stdout:
return if ok {
case line = <-stdout: break
case line = <-stderr: }
errLine = true if stderr == nil {
return
}
stdout = nil
case line, ok = <-stderr:
if ok {
errLine = true
break
}
if stdout == nil {
return
}
stderr = nil
} }
line, level := processLogLine(line) line, level := processLogLine(line)
if line == "" { if line == "" {
+21 -15
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/pmtud/tcp (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/pmtud/tcp (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=tcp . Logger
//
// Package tcp is a generated GoMock package. // Package tcp is a generated GoMock package.
package tcp package tcp
@@ -7,13 +12,14 @@ package tcp
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,47 +40,47 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Debug mocks base method. // Debug mocks base method.
func (m *MockLogger) Debug(arg0 string) { func (m *MockLogger) Debug(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Debug", arg0) m.ctrl.Call(m, "Debug", msg)
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), msg)
} }
// Debugf mocks base method. // Debugf mocks base method.
func (m *MockLogger) Debugf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Debugf(msg string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{msg}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Debugf", varargs...) m.ctrl.Call(m, "Debugf", varargs...)
} }
// Debugf indicates an expected call of Debugf. // Debugf indicates an expected call of Debugf.
func (mr *MockLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debugf(msg any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{msg}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...)
} }
// Warnf mocks base method. // Warnf mocks base method.
func (m *MockLogger) Warnf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Warnf(msg string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{msg}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Warnf", varargs...) m.ctrl.Call(m, "Warnf", varargs...)
} }
// Warnf indicates an expected call of Warnf. // Warnf indicates an expected call of Warnf.
func (mr *MockLoggerMockRecorder) Warnf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warnf(msg any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{msg}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockLogger)(nil).Warnf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockLogger)(nil).Warnf), varargs...)
} }
+1 -1
View File
@@ -8,11 +8,11 @@ import (
"testing" "testing"
"time" "time"
gomock "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/netlink" "github.com/qdm12/gluetun/internal/netlink"
"github.com/qdm12/gluetun/internal/pmtud/constants" "github.com/qdm12/gluetun/internal/pmtud/constants"
"github.com/qdm12/gluetun/internal/pmtud/ip" "github.com/qdm12/gluetun/internal/pmtud/ip"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
) )
func Test_runTest(t *testing.T) { func Test_runTest(t *testing.T) {
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"context" "context"
"testing" "testing"
gomock "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/command" "github.com/qdm12/gluetun/internal/command"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
gomock "go.uber.org/mock/gomock"
) )
func Test_Service_runCommand(t *testing.T) { func Test_Service_runCommand(t *testing.T) {
+23 -17
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/portforward/service (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/portforward/service (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=service . Logger
//
// Package service is a generated GoMock package. // Package service is a generated GoMock package.
package service package service
@@ -7,13 +12,14 @@ package service
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,49 +40,49 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Debug mocks base method. // Debug mocks base method.
func (m *MockLogger) Debug(arg0 string) { func (m *MockLogger) Debug(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Debug", arg0) m.ctrl.Call(m, "Debug", s)
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), s)
} }
// Error mocks base method. // Error mocks base method.
func (m *MockLogger) Error(arg0 string) { func (m *MockLogger) Error(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Error", arg0) m.ctrl.Call(m, "Error", s)
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), s)
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", s)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), s)
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockLogger) Warn(arg0 string) { func (m *MockLogger) Warn(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", s)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warn(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), s)
} }
+1 -1
View File
@@ -3,7 +3,7 @@ package pprof
import ( import (
"regexp" "regexp"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
func boolPtr(b bool) *bool { return &b } func boolPtr(b bool) *bool { return &b }
+19 -13
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/httpserver (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/httpserver (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=logger_mock_test.go -package pprof github.com/qdm12/gluetun/internal/httpserver Logger
//
// Package pprof is a generated GoMock package. // Package pprof is a generated GoMock package.
package pprof package pprof
@@ -7,13 +12,14 @@ package pprof
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,37 +40,37 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Error mocks base method. // Error mocks base method.
func (m *MockLogger) Error(arg0 string) { func (m *MockLogger) Error(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Error", arg0) m.ctrl.Call(m, "Error", msg)
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), msg)
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", msg)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), msg)
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockLogger) Warn(arg0 string) { func (m *MockLogger) Warn(msg string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", msg)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warn(msg any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), msg)
} }
+1 -1
View File
@@ -7,10 +7,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/httpserver" "github.com/qdm12/gluetun/internal/httpserver"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
//go:generate mockgen -destination=logger_mock_test.go -package $GOPACKAGE github.com/qdm12/gluetun/internal/httpserver Logger //go:generate mockgen -destination=logger_mock_test.go -package $GOPACKAGE github.com/qdm12/gluetun/internal/httpserver Logger
+26 -17
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/provider/common (interfaces: ParallelResolver,Storage,Unzipper,Warner) // Source: github.com/qdm12/gluetun/internal/provider/common (interfaces: ParallelResolver,Storage,Unzipper,Warner)
//
// Generated by this command:
//
// mockgen -destination=mocks.go -package common . ParallelResolver,Storage,Unzipper,Warner
//
// Package common is a generated GoMock package. // Package common is a generated GoMock package.
package common package common
@@ -9,16 +14,17 @@ import (
netip "net/netip" netip "net/netip"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
settings "github.com/qdm12/gluetun/internal/configuration/settings" settings "github.com/qdm12/gluetun/internal/configuration/settings"
models "github.com/qdm12/gluetun/internal/models" models "github.com/qdm12/gluetun/internal/models"
resolver "github.com/qdm12/gluetun/internal/updater/resolver" resolver "github.com/qdm12/gluetun/internal/updater/resolver"
gomock "go.uber.org/mock/gomock"
) )
// MockParallelResolver is a mock of ParallelResolver interface. // MockParallelResolver is a mock of ParallelResolver interface.
type MockParallelResolver struct { type MockParallelResolver struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockParallelResolverMockRecorder recorder *MockParallelResolverMockRecorder
isgomock struct{}
} }
// MockParallelResolverMockRecorder is the mock recorder for MockParallelResolver. // MockParallelResolverMockRecorder is the mock recorder for MockParallelResolver.
@@ -39,9 +45,9 @@ func (m *MockParallelResolver) EXPECT() *MockParallelResolverMockRecorder {
} }
// Resolve mocks base method. // Resolve mocks base method.
func (m *MockParallelResolver) Resolve(arg0 context.Context, arg1 resolver.ParallelSettings) (map[string][]netip.Addr, []string, error) { func (m *MockParallelResolver) Resolve(ctx context.Context, arg1 resolver.ParallelSettings) (map[string][]netip.Addr, []string, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Resolve", arg0, arg1) ret := m.ctrl.Call(m, "Resolve", ctx, arg1)
ret0, _ := ret[0].(map[string][]netip.Addr) ret0, _ := ret[0].(map[string][]netip.Addr)
ret1, _ := ret[1].([]string) ret1, _ := ret[1].([]string)
ret2, _ := ret[2].(error) ret2, _ := ret[2].(error)
@@ -49,15 +55,16 @@ func (m *MockParallelResolver) Resolve(arg0 context.Context, arg1 resolver.Paral
} }
// Resolve indicates an expected call of Resolve. // Resolve indicates an expected call of Resolve.
func (mr *MockParallelResolverMockRecorder) Resolve(arg0, arg1 interface{}) *gomock.Call { func (mr *MockParallelResolverMockRecorder) Resolve(ctx, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockParallelResolver)(nil).Resolve), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockParallelResolver)(nil).Resolve), ctx, arg1)
} }
// MockStorage is a mock of Storage interface. // MockStorage is a mock of Storage interface.
type MockStorage struct { type MockStorage struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockStorageMockRecorder recorder *MockStorageMockRecorder
isgomock struct{}
} }
// MockStorageMockRecorder is the mock recorder for MockStorage. // MockStorageMockRecorder is the mock recorder for MockStorage.
@@ -78,24 +85,25 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
} }
// FilterServers mocks base method. // FilterServers mocks base method.
func (m *MockStorage) FilterServers(arg0 string, arg1 settings.ServerSelection) ([]models.Server, error) { func (m *MockStorage) FilterServers(provider string, selection settings.ServerSelection) ([]models.Server, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FilterServers", arg0, arg1) ret := m.ctrl.Call(m, "FilterServers", provider, selection)
ret0, _ := ret[0].([]models.Server) ret0, _ := ret[0].([]models.Server)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// FilterServers indicates an expected call of FilterServers. // FilterServers indicates an expected call of FilterServers.
func (mr *MockStorageMockRecorder) FilterServers(arg0, arg1 interface{}) *gomock.Call { func (mr *MockStorageMockRecorder) FilterServers(provider, selection any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterServers", reflect.TypeOf((*MockStorage)(nil).FilterServers), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterServers", reflect.TypeOf((*MockStorage)(nil).FilterServers), provider, selection)
} }
// MockUnzipper is a mock of Unzipper interface. // MockUnzipper is a mock of Unzipper interface.
type MockUnzipper struct { type MockUnzipper struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockUnzipperMockRecorder recorder *MockUnzipperMockRecorder
isgomock struct{}
} }
// MockUnzipperMockRecorder is the mock recorder for MockUnzipper. // MockUnzipperMockRecorder is the mock recorder for MockUnzipper.
@@ -116,24 +124,25 @@ func (m *MockUnzipper) EXPECT() *MockUnzipperMockRecorder {
} }
// FetchAndExtract mocks base method. // FetchAndExtract mocks base method.
func (m *MockUnzipper) FetchAndExtract(arg0 context.Context, arg1 string) (map[string][]byte, error) { func (m *MockUnzipper) FetchAndExtract(ctx context.Context, url string) (map[string][]byte, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchAndExtract", arg0, arg1) ret := m.ctrl.Call(m, "FetchAndExtract", ctx, url)
ret0, _ := ret[0].(map[string][]byte) ret0, _ := ret[0].(map[string][]byte)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// FetchAndExtract indicates an expected call of FetchAndExtract. // FetchAndExtract indicates an expected call of FetchAndExtract.
func (mr *MockUnzipperMockRecorder) FetchAndExtract(arg0, arg1 interface{}) *gomock.Call { func (mr *MockUnzipperMockRecorder) FetchAndExtract(ctx, url any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchAndExtract", reflect.TypeOf((*MockUnzipper)(nil).FetchAndExtract), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchAndExtract", reflect.TypeOf((*MockUnzipper)(nil).FetchAndExtract), ctx, url)
} }
// MockWarner is a mock of Warner interface. // MockWarner is a mock of Warner interface.
type MockWarner struct { type MockWarner struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockWarnerMockRecorder recorder *MockWarnerMockRecorder
isgomock struct{}
} }
// MockWarnerMockRecorder is the mock recorder for MockWarner. // MockWarnerMockRecorder is the mock recorder for MockWarner.
@@ -154,13 +163,13 @@ func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockWarner) Warn(arg0 string) { func (m *MockWarner) Warn(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", s)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockWarnerMockRecorder) Warn(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), s)
} }
@@ -5,7 +5,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
@@ -13,6 +12,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_Provider_GetConnection(t *testing.T) { func Test_Provider_GetConnection(t *testing.T) {
@@ -7,13 +7,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/qdm12/gluetun/internal/updater/resolver" "github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_Updater_GetServers(t *testing.T) { func Test_Updater_GetServers(t *testing.T) {
+1 -1
View File
@@ -6,7 +6,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
@@ -14,6 +13,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_Provider_GetConnection(t *testing.T) { func Test_Provider_GetConnection(t *testing.T) {
@@ -10,13 +10,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/qdm12/gluetun/internal/updater/resolver" "github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_Updater_GetServers(t *testing.T) { func Test_Updater_GetServers(t *testing.T) {
+1 -1
View File
@@ -6,7 +6,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
@@ -14,6 +13,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_Provider_GetConnection(t *testing.T) { func Test_Provider_GetConnection(t *testing.T) {
+1 -1
View File
@@ -6,7 +6,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
@@ -14,6 +13,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_GetConnection(t *testing.T) { func Test_GetConnection(t *testing.T) {
@@ -7,10 +7,10 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"golang.org/x/net/html" "golang.org/x/net/html"
) )
@@ -6,7 +6,6 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/configuration/settings" "github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
@@ -14,6 +13,7 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
) )
func Test_Provider_GetConnection(t *testing.T) { func Test_Provider_GetConnection(t *testing.T) {
+82
View File
@@ -0,0 +1,82 @@
package restrictednet
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"strconv"
"github.com/qdm12/dns/v2/pkg/provider"
)
// Client is a client for making restricted network requests,
// such as opening temporary firewall rules for HTTPS connections.
// It is not meant to be high performance, although it can be used for
// multiple requests and concurrently.
type Client struct {
outboundInterface string
ipv6Supported bool
firewall Firewall
dohServers []provider.DoHServer
}
func New(settings Settings) *Client {
if err := settings.validate(); err != nil {
panic(fmt.Sprintf("invalid settings: %v", err)) // programming error
}
dohServers := make([]provider.DoHServer, len(settings.UpstreamResolvers))
for i, upstreamResolver := range settings.UpstreamResolvers {
dohServers[i] = upstreamResolver.DoH
}
return &Client{
outboundInterface: settings.DefaultInterface,
ipv6Supported: *settings.IPv6Supported,
firewall: settings.Firewall,
dohServers: dohServers,
}
}
// OpenHTTPSByHostname opens an https connection through the firewall,
// to the hostname which in the format `host:port`. The returned cleanup
// function must be called to remove the temporary firewall rule and close connections.
// It first resolves the domain in hostname using DNS over HTTPS and then opens
// the restricted HTTPS connection to the resolved IP.
func (c *Client) OpenHTTPSByHostname(ctx context.Context, hostname string) (
httpClient *http.Client, cleanup func() error, err error,
) {
host, portStr, err := net.SplitHostPort(hostname)
if err != nil {
return nil, nil, fmt.Errorf("splitting host and port: %w", err)
}
resolvedIPs, err := c.ResolveName(ctx, host)
if err != nil {
return nil, nil, fmt.Errorf("resolving name: %w", err)
} else if len(resolvedIPs) == 0 {
return nil, nil, fmt.Errorf("no IP address found for name %q", host)
}
portUint, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, nil, fmt.Errorf("parsing port: %w", err)
} else if portUint == 0 {
return nil, nil, errors.New("destination port cannot be 0")
}
port := uint16(portUint)
errs := make([]error, 0, len(resolvedIPs))
for _, ip := range resolvedIPs {
addrPort := netip.AddrPortFrom(ip, port)
httpClient, cleanup, err := c.OpenHTTPS(ctx, host, addrPort)
if err != nil {
errs = append(errs, fmt.Errorf("for %s: %w", ip, err))
continue
}
return httpClient, cleanup, nil
}
return nil, nil, fmt.Errorf("opening HTTPS to %s: %w", hostname, errors.Join(errs...))
}
+7
View File
@@ -0,0 +1,7 @@
//go:build integration
package restrictednet
func ptrTo[T any](value T) *T {
return &value
}
+202
View File
@@ -0,0 +1,202 @@
package restrictednet
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"os"
"time"
"github.com/jsimonetti/rtnetlink"
"github.com/qdm12/gluetun/internal/pmtud/constants"
)
// OpenHTTPS opens temporary restrictive firewall output for one HTTPS destination.
// The returned [*http.Client] must be used sequentially only, and each request must
// have its response body fully read/discarded and then closed.
// The returned cleanup function must be called to remove the temporary firewall rule and close connections.
func (c *Client) OpenHTTPS(ctx context.Context, destinationTLSName string, destinationAddrPort netip.AddrPort,
) (httpClient *http.Client, cleanup func() error, err error) {
fd, sourceAddrPort, err := bindSourceConnection(destinationAddrPort.Addr())
if err != nil {
return nil, nil, fmt.Errorf("binding source port: %w", err)
}
const remove = false
err = c.firewall.AcceptOutputFromIPPortToIPPort(ctx, "tcp", c.outboundInterface,
sourceAddrPort, destinationAddrPort, remove)
if err != nil {
closeFD(fd)
return nil, nil, fmt.Errorf("allowing output traffic through firewall: %w", err)
}
connection, err := connectSourceConnection(ctx, fd, destinationAddrPort)
if err != nil {
const remove = true
_ = c.firewall.AcceptOutputFromIPPortToIPPort(context.Background(), "tcp", c.outboundInterface,
sourceAddrPort, destinationAddrPort, remove)
return nil, nil, fmt.Errorf("connecting source socket: %w", err)
}
dial := makeDial(connection, destinationTLSName)
httpClient = newHTTPSClient(destinationTLSName, dial)
cleanup = func() error {
var errs []error
httpClient.CloseIdleConnections()
err := connection.Close()
if err != nil && !errors.Is(err, net.ErrClosed) {
errs = append(errs, fmt.Errorf("closing connection: %w", err))
}
const remove = true
err = c.firewall.AcceptOutputFromIPPortToIPPort(context.Background(), "tcp", c.outboundInterface,
sourceAddrPort, destinationAddrPort, remove)
if err != nil {
errs = append(errs, fmt.Errorf("removing output traffic rule: %w", err))
}
if len(errs) > 0 {
return errors.Join(errs...)
}
return nil
}
return httpClient, cleanup, nil
}
type dialFunc func(ctx context.Context, network, address string) (net.Conn, error)
func newHTTPSClient(destinationTLSName string, dial dialFunc) *http.Client {
const timeout = 5 * time.Second
transport := &http.Transport{
MaxIdleConns: 1,
MaxIdleConnsPerHost: 1,
MaxConnsPerHost: 1,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: destinationTLSName,
},
DialContext: dial,
}
return &http.Client{
Timeout: timeout,
Transport: transport,
}
}
func makeDial(connection net.Conn, tlsName string) dialFunc {
_, destinationPort, err := net.SplitHostPort(connection.RemoteAddr().String())
if err != nil {
panic(err) // connection remote address should always be in the form "host:port"
}
expectedAddress := net.JoinHostPort(tlsName, destinationPort)
used := false
return func(_ context.Context, network, address string) (net.Conn, error) {
if used {
return nil, errors.New("dial function called more than once")
}
used = true
switch network {
case "tcp", "tcp4", "tcp6":
default:
return nil, fmt.Errorf("unexpected dial network %q", network)
}
if address != expectedAddress {
return nil, fmt.Errorf("unexpected dial address %q (expected %q)", address, expectedAddress)
}
return connection, nil
}
}
func bindSourceConnection(destinationIP netip.Addr) (fd int, sourceAddr netip.AddrPort, err error) {
sourceIP, err := sourceIPForDestination(destinationIP)
if err != nil {
return 0, netip.AddrPort{}, fmt.Errorf("finding source IP: %w", err)
}
family := constants.AF_INET
if sourceIP.Is6() {
family = constants.AF_INET6
}
fd, err = newTCPSockStream(family)
if err != nil {
return 0, netip.AddrPort{}, fmt.Errorf("creating socket: %w", err)
}
bindAddrPort := netip.AddrPortFrom(sourceIP, 0)
err = bindFD(fd, bindAddrPort)
if err != nil {
closeFD(fd)
return 0, netip.AddrPort{}, fmt.Errorf("binding socket: %w", err)
}
sourceAddr, err = fdToSourceAddr(fd)
if err != nil {
closeFD(fd)
return 0, netip.AddrPort{}, fmt.Errorf("getting source address: %w", err)
}
return fd, sourceAddr, nil
}
func connectSourceConnection(ctx context.Context, fd int, destinationAddrPort netip.AddrPort) (
connection net.Conn, err error,
) {
err = connectFD(ctx, fd, destinationAddrPort)
if err != nil {
closeFD(fd)
return nil, fmt.Errorf("connecting socket: %w", err)
}
file := os.NewFile(uintptr(fd), "")
if file == nil {
closeFD(fd)
return nil, fmt.Errorf("creating socket file for destination %s", destinationAddrPort)
}
defer file.Close()
connection, err = net.FileConn(file)
if err != nil {
return nil, fmt.Errorf("wrapping socket connection: %w", err)
}
return connection, nil
}
func sourceIPForDestination(destinationIP netip.Addr) (srcIP netip.Addr, err error) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return netip.Addr{}, err
}
defer conn.Close()
family := uint8(constants.AF_INET)
if destinationIP.Is6() {
family = constants.AF_INET6
}
requestMessage := &rtnetlink.RouteMessage{
Family: family,
Attributes: rtnetlink.RouteAttributes{
Dst: destinationIP.AsSlice(),
},
}
messages, err := conn.Route.Get(requestMessage)
if err != nil {
return netip.Addr{}, fmt.Errorf("getting routes to %s: %w", destinationIP, err)
}
for _, message := range messages {
if message.Attributes.Src == nil {
continue
}
if message.Attributes.Src.To4() == nil {
return netip.AddrFrom16([16]byte(message.Attributes.Src)), nil
}
return netip.AddrFrom4([4]byte(message.Attributes.Src)), nil
}
return netip.Addr{}, fmt.Errorf("no route to %s", destinationIP)
}
@@ -0,0 +1,117 @@
//go:build integration
package restrictednet
import (
"context"
"fmt"
"io"
"net/http"
"net/netip"
"testing"
"github.com/qdm12/dns/v2/pkg/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
type listenAddrPortMatcher struct {
expected netip.AddrPort
}
func (m listenAddrPortMatcher) Matches(x any) bool {
ip, ok := x.(netip.AddrPort)
if !ok {
return false
}
if m.expected.IsValid() {
return ip == m.expected
}
return ip.IsValid() && ip.Addr().IsValid() && ip.Port() > 0
}
func (m listenAddrPortMatcher) String() string {
if m.expected.IsValid() {
return "is the same as " + m.expected.String()
}
return "is a valid netip.AddrPort with a valid IP and non-zero port"
}
type destinationAddrPortMatcher struct {
expected netip.AddrPort
}
func (m destinationAddrPortMatcher) Matches(x any) bool {
ip, ok := x.(netip.AddrPort)
if !ok {
return false
}
if m.expected.IsValid() {
return ip == m.expected
}
return ip.IsValid() && ip.Port() == m.expected.Port()
}
func (m destinationAddrPortMatcher) String() string {
if m.expected.IsValid() {
return "is the same as " + m.expected.String()
}
return "matches the port " + fmt.Sprint(m.expected.Port())
}
func Test_Client_OpenHTTPS(t *testing.T) {
t.Parallel()
ctx := t.Context()
ctrl := gomock.NewController(t)
const destinationTLSName = "one.one.one.one"
destinationAddrPort := netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 1, 1, 1}), 443)
firewall := NewMockFirewall(ctrl)
sourceMatcher := listenAddrPortMatcher{}
firewall.EXPECT().AcceptOutputFromIPPortToIPPort(
ctx, "tcp", "eth0", sourceMatcher, destinationAddrPort, false,
).DoAndReturn(func(_ context.Context,
_, _ string, source, _ netip.AddrPort, _ bool,
) error {
sourceMatcher.expected = source
return nil
})
firewall.EXPECT().AcceptOutputFromIPPortToIPPort(
context.Background(), "tcp", "eth0", sourceMatcher, destinationAddrPort, true,
).Return(nil)
const ipv6Supported = false
upstreamResolvers := []provider.Provider{provider.Google()}
settings := Settings{
Firewall: firewall,
DefaultInterface: "eth0",
IPv6Supported: ptrTo(ipv6Supported),
UpstreamResolvers: upstreamResolvers,
}
client := New(settings)
httpClient, cleanup, err := client.OpenHTTPS(ctx, destinationTLSName, destinationAddrPort)
require.NoError(t, err)
require.NotNil(t, httpClient)
require.NotNil(t, cleanup)
const requests = 2
for range requests {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://"+destinationTLSName, nil)
require.NoError(t, err)
response, err := httpClient.Do(request)
require.NoError(t, err)
_, err = io.Copy(io.Discard, response.Body)
require.NoError(t, err)
err = response.Body.Close()
require.NoError(t, err)
assert.Equal(t, http.StatusOK, response.StatusCode)
}
err = cleanup()
require.NoError(t, err)
}
+12
View File
@@ -0,0 +1,12 @@
package restrictednet
import (
"context"
"net/netip"
)
type Firewall interface {
AcceptOutputFromIPPortToIPPort(ctx context.Context,
protocol, intf string, source, destination netip.AddrPort, remove bool,
) error
}
+121
View File
@@ -0,0 +1,121 @@
//go:build linux
package restrictednet
import (
"context"
"errors"
"fmt"
"net/netip"
"time"
"golang.org/x/sys/unix"
)
func closeFD(fd int) {
unix.Close(fd)
}
func newTCPSockStream(family int) (fd int, err error) {
fd, err = unix.Socket(family, unix.SOCK_STREAM, unix.IPPROTO_TCP)
if err != nil {
return 0, err
}
err = unix.SetNonblock(fd, true)
if err != nil {
_ = unix.Close(fd)
return 0, err
}
return fd, nil
}
func bindFD(fd int, address netip.AddrPort) error {
bindAddr := makeSockAddr(address)
return unix.Bind(fd, bindAddr)
}
func connectFD(ctx context.Context, fd int, destination netip.AddrPort) error {
err := unix.Connect(fd, makeSockAddr(destination))
switch {
case err == nil:
return nil
case !errors.Is(err, unix.EINPROGRESS):
return err
}
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
bitsIndex := fd / 64 //nolint:mnd
if bitsIndex >= len(unix.FdSet{}.Bits) {
return fmt.Errorf("fd %d exceeds unix.Select FdSet capacity", fd)
}
wset := &unix.FdSet{}
wset.Bits[bitsIndex] |= 1 << (uint64(fd) % 64) //nolint:gosec,mnd
eset := &unix.FdSet{}
eset.Bits[bitsIndex] |= 1 << (uint64(fd) % 64) //nolint:gosec,mnd
const selectTimeout = 50 * time.Millisecond
timeval := unix.NsecToTimeval(int64(selectTimeout))
// Wait for the FD to become writable or hit an error state
n, err := unix.Select(fd+1, nil, wset, eset, &timeval)
if err != nil {
if errors.Is(err, unix.EINTR) {
continue // Syscall interrupted, try again
}
return fmt.Errorf("select error: %w", err)
} else if n == 0 {
continue // no status change yet
}
// Check if the socket encountered an error
n, err = unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_ERROR)
if err != nil {
return fmt.Errorf("getsockopt error: %w", err)
} else if n != 0 {
return fmt.Errorf("connect failed asynchronously: %w", unix.Errno(n))
}
return nil
}
}
}
func fdToSourceAddr(fd int) (sourceAddrPort netip.AddrPort, err error) {
sockAddr, err := unix.Getsockname(fd)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("getting sockname: %w", err)
}
sourceAddrPort, err = sockAddrToAddrPort(sockAddr)
if err != nil {
return netip.AddrPort{}, err
}
return sourceAddrPort, nil
}
func makeSockAddr(addressPort netip.AddrPort) unix.Sockaddr {
if addressPort.Addr().Is4() {
return &unix.SockaddrInet4{
Port: int(addressPort.Port()),
Addr: addressPort.Addr().As4(),
}
}
return &unix.SockaddrInet6{
Port: int(addressPort.Port()),
Addr: addressPort.Addr().As16(),
}
}
func sockAddrToAddrPort(sockAddr unix.Sockaddr) (addrPort netip.AddrPort, err error) {
switch typedSockAddr := sockAddr.(type) {
case *unix.SockaddrInet4:
return netip.AddrPortFrom(netip.AddrFrom4(typedSockAddr.Addr), uint16(typedSockAddr.Port)), nil //nolint:gosec
case *unix.SockaddrInet6:
return netip.AddrPortFrom(netip.AddrFrom16(typedSockAddr.Addr), uint16(typedSockAddr.Port)), nil //nolint:gosec
default:
return netip.AddrPort{}, fmt.Errorf("unexpected socket address type %T", typedSockAddr)
}
}
@@ -0,0 +1,3 @@
package restrictednet
//go:generate mockgen -destination=mocks_test.go -package=$GOPACKAGE . Firewall
+56
View File
@@ -0,0 +1,56 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/restrictednet (interfaces: Firewall)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=restrictednet . Firewall
//
// Package restrictednet is a generated GoMock package.
package restrictednet
import (
context "context"
netip "net/netip"
reflect "reflect"
gomock "go.uber.org/mock/gomock"
)
// MockFirewall is a mock of Firewall interface.
type MockFirewall struct {
ctrl *gomock.Controller
recorder *MockFirewallMockRecorder
isgomock struct{}
}
// MockFirewallMockRecorder is the mock recorder for MockFirewall.
type MockFirewallMockRecorder struct {
mock *MockFirewall
}
// NewMockFirewall creates a new mock instance.
func NewMockFirewall(ctrl *gomock.Controller) *MockFirewall {
mock := &MockFirewall{ctrl: ctrl}
mock.recorder = &MockFirewallMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockFirewall) EXPECT() *MockFirewallMockRecorder {
return m.recorder
}
// AcceptOutputFromIPPortToIPPort mocks base method.
func (m *MockFirewall) AcceptOutputFromIPPortToIPPort(ctx context.Context, protocol, intf string, source, destination netip.AddrPort, remove bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AcceptOutputFromIPPortToIPPort", ctx, protocol, intf, source, destination, remove)
ret0, _ := ret[0].(error)
return ret0
}
// AcceptOutputFromIPPortToIPPort indicates an expected call of AcceptOutputFromIPPortToIPPort.
func (mr *MockFirewallMockRecorder) AcceptOutputFromIPPortToIPPort(ctx, protocol, intf, source, destination, remove any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptOutputFromIPPortToIPPort", reflect.TypeOf((*MockFirewall)(nil).AcceptOutputFromIPPortToIPPort), ctx, protocol, intf, source, destination, remove)
}
+28
View File
@@ -0,0 +1,28 @@
//go:build !linux
package restrictednet
import (
"context"
"net/netip"
)
func closeFD(fd int) {
panic("not implemented")
}
func newTCPSockStream(family int) (fd int, err error) {
panic("not implemented")
}
func bindFD(fd int, address netip.AddrPort) error {
panic("not implemented")
}
func connectFD(ctx context.Context, fd int, destination netip.AddrPort) error {
panic("not implemented")
}
func fdToSourceAddr(fd int) (sourceAddrPort netip.AddrPort, err error) {
panic("not implemented")
}
+201
View File
@@ -0,0 +1,201 @@
package restrictednet
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"net/netip"
"net/url"
"strconv"
"github.com/miekg/dns"
)
// ResolveName resolves the given host name to IP addresses using DoH servers,
// while opening temporary restrictive firewall rules for HTTPS traffic to DoH servers.
// The host must be a single well-formed domain name, without port or path.
func (c *Client) ResolveName(ctx context.Context, host string) (
resolvedAddresses []netip.Addr, err error,
) {
const maxTypes = 2
questionTypes := make([]uint16, 0, maxTypes)
if c.ipv6Supported {
questionTypes = append(questionTypes, dns.TypeAAAA)
}
questionTypes = append(questionTypes, dns.TypeA)
var addresses []netip.Addr
errs := make([]error, 0, len(questionTypes))
for _, questionType := range questionTypes {
answerAddresses, err := c.resolveOneQuestionType(ctx, host, questionType)
if err != nil {
errs = append(errs, err)
continue
}
addresses = append(addresses, answerAddresses...)
}
switch {
case len(addresses) > 0:
return addresses, nil
case len(errs) == 0:
return nil, nil // no address found
default: // errors
return nil, fmt.Errorf("resolving host %q: %w", host, errors.Join(errs...))
}
}
func (c *Client) resolveOneQuestionType(ctx context.Context,
host string, questionType uint16,
) (addresses []netip.Addr, err error) {
queryMessage := &dns.Msg{}
queryMessage.SetQuestion(dns.Fqdn(host), questionType)
queryWire, err := queryMessage.Pack()
if err != nil {
return nil, fmt.Errorf("packing DNS query: %w", err)
}
// Try every DoH server and every of each of their IP until we get a non-empty
// successful response.
errs := make([]error, 0)
for _, dohServer := range c.dohServers {
dohURL, err := url.Parse(dohServer.URL)
if err != nil {
errs = append(errs,
fmt.Errorf("parsing DoH server URL %s: %w", dohServer.URL, err))
continue
}
dohServerIPs := make([]netip.Addr, 0, len(dohServer.IPv4)+len(dohServer.IPv6))
if c.ipv6Supported {
// Prefer IPv6 addresses if IPv6 is supported
dohServerIPs = append(dohServerIPs, dohServer.IPv6...)
}
dohServerIPs = append(dohServerIPs, dohServer.IPv4...)
for _, dohServerIP := range dohServerIPs {
const defaultDoHPort uint16 = 443
port := defaultDoHPort
if portStr := dohURL.Port(); portStr != "" {
port, err = parseDestinationPort(portStr)
if err != nil {
errs = append(errs, fmt.Errorf("parsing DoH server port: %w", err))
continue
}
}
dohServerAddrPort := netip.AddrPortFrom(dohServerIP, port)
responseMessage, err := c.doHQuery(ctx, queryWire, dohURL, dohServerAddrPort)
switch {
case err != nil:
errs = append(errs, fmt.Errorf("querying DoH server %q (%s): %w",
dohServer.URL, dohServerAddrPort, err))
continue
case responseMessage.Rcode != dns.RcodeSuccess:
errs = append(errs, fmt.Errorf("querying DoH server %q (%s): DNS rcode %s",
dohServer.URL, dohServerAddrPort, dns.RcodeToString[responseMessage.Rcode]))
continue
}
addresses := answersToNetipAddrs(responseMessage)
if len(addresses) == 0 {
continue
}
return addresses, nil
}
}
if len(errs) == 0 {
return nil, nil
}
return nil, fmt.Errorf("resolving %s %s: %w",
dns.TypeToString[questionType], host, errors.Join(errs...))
}
func (c *Client) doHQuery(ctx context.Context, queryWire []byte,
dohURL *url.URL, dohServerAddrPort netip.AddrPort,
) (responseMessage *dns.Msg, err error) {
httpClient, cleanup, err := c.OpenHTTPS(ctx, dohURL.Hostname(), dohServerAddrPort)
if err != nil {
return nil, fmt.Errorf("opening https connection: %w", err)
}
defer func() {
closeErr := cleanup()
if err == nil && closeErr != nil {
err = fmt.Errorf("cleaning up https connection: %w", closeErr)
}
}()
requestBody := bytes.NewReader(queryWire)
request, err := http.NewRequestWithContext(ctx, http.MethodPost, dohURL.String(), requestBody)
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
request.Header.Set("Content-Type", "application/dns-message")
request.Header.Set("Accept", "application/dns-message")
response, err := httpClient.Do(request)
if err != nil {
return nil, err
}
responseData, err := io.ReadAll(response.Body)
if err != nil {
_ = response.Body.Close()
return nil, fmt.Errorf("reading response body: %w", err)
}
err = response.Body.Close()
if err != nil {
return nil, fmt.Errorf("closing response body: %w", err)
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("response status code is %s (data length %d)",
response.Status, len(responseData))
}
responseMessage = new(dns.Msg)
err = responseMessage.Unpack(responseData)
if err != nil {
return nil, fmt.Errorf("parsing DoH response: %w", err)
}
return responseMessage, nil
}
func answersToNetipAddrs(message *dns.Msg) (addresses []netip.Addr) {
if message == nil {
return nil
}
addresses = make([]netip.Addr, 0, len(message.Answer))
for _, answer := range message.Answer {
switch record := answer.(type) {
case *dns.A:
address, ok := netip.AddrFromSlice(record.A)
if ok {
addresses = append(addresses, address.Unmap())
}
case *dns.AAAA:
address, ok := netip.AddrFromSlice(record.AAAA)
if ok {
addresses = append(addresses, address)
}
}
}
return addresses
}
func parseDestinationPort(portStr string) (port uint16, err error) {
portUint, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return 0, err
}
if portUint == 0 {
return 0, errors.New("port cannot be 0")
}
return uint16(portUint), nil
}
@@ -0,0 +1,110 @@
//go:build integration
package restrictednet
import (
"context"
"net"
"net/netip"
"testing"
"github.com/miekg/dns"
"github.com/qdm12/dns/v2/pkg/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func Test_Client_ResolveName(t *testing.T) {
t.Parallel()
ctx := t.Context()
ctrl := gomock.NewController(t)
firewall := NewMockFirewall(ctrl)
sourceMatcher := listenAddrPortMatcher{}
destinationMatcher := destinationAddrPortMatcher{
expected: netip.AddrPortFrom(netip.Addr{}, 443),
}
// Add rule
firstCall := firewall.EXPECT().AcceptOutputFromIPPortToIPPort(
ctx, "tcp", "eth0", sourceMatcher, destinationMatcher, false,
).DoAndReturn(func(
_ context.Context, _, _ string, source, destination netip.AddrPort, _ bool,
) error {
sourceMatcher.expected = source
destinationMatcher.expected = destination
return nil
})
// Removal rule
firewall.EXPECT().AcceptOutputFromIPPortToIPPort(
context.Background(), "tcp", "eth0", sourceMatcher, destinationMatcher, true,
).Return(nil).After(firstCall)
settings := Settings{
DefaultInterface: "eth0",
IPv6Supported: ptrTo(false),
Firewall: firewall,
UpstreamResolvers: []provider.Provider{provider.Cloudflare()},
}
client := New(settings)
addresses, err := client.ResolveName(ctx, "github.com")
require.NoError(t, err)
assert.NotEmpty(t, addresses)
}
func Test_answersToNetipAddrs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
message *dns.Msg
expected []netip.Addr
}{
"nil_message": {},
"no_answers": {
message: &dns.Msg{},
expected: []netip.Addr{},
},
"a_record": {
message: &dns.Msg{Answer: []dns.RR{
&dns.A{
Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Class: dns.ClassINET},
A: net.IP{1, 1, 1, 1},
},
}},
expected: []netip.Addr{netip.MustParseAddr("1.1.1.1")},
},
"aaaa_record": {
message: &dns.Msg{Answer: []dns.RR{
&dns.AAAA{
Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET},
AAAA: net.IP{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88},
},
}},
expected: []netip.Addr{netip.MustParseAddr("2001:4860:4860::8888")},
},
"mixed_records": {
message: &dns.Msg{Answer: []dns.RR{
&dns.A{
Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeA, Class: dns.ClassINET},
A: net.IP{1, 1, 1, 1},
},
&dns.AAAA{
Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET},
AAAA: net.IP{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88},
},
}},
expected: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("2001:4860:4860::8888")},
},
}
for testName, testCase := range testCases {
t.Run(testName, func(t *testing.T) {
t.Parallel()
addresses := answersToNetipAddrs(testCase.message)
assert.Equal(t, testCase.expected, addresses)
})
}
}
+28
View File
@@ -0,0 +1,28 @@
package restrictednet
import (
"errors"
"github.com/qdm12/dns/v2/pkg/provider"
)
type Settings struct {
DefaultInterface string
IPv6Supported *bool
Firewall Firewall
UpstreamResolvers []provider.Provider
}
func (s *Settings) validate() error {
switch {
case s.DefaultInterface == "":
return errors.New("default interface is not set")
case s.IPv6Supported == nil:
return errors.New("IPv6 support field is not set")
case s.Firewall == nil:
return errors.New("firewall is not set")
case len(s.UpstreamResolvers) == 0:
return errors.New("no upstream resolvers provided")
}
return nil
}
+67 -61
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/routing (interfaces: NetLinker) // Source: github.com/qdm12/gluetun/internal/routing (interfaces: NetLinker)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=routing . NetLinker
//
// Package routing is a generated GoMock package. // Package routing is a generated GoMock package.
package routing package routing
@@ -8,14 +13,15 @@ import (
netip "net/netip" netip "net/netip"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
netlink "github.com/qdm12/gluetun/internal/netlink" netlink "github.com/qdm12/gluetun/internal/netlink"
gomock "go.uber.org/mock/gomock"
) )
// MockNetLinker is a mock of NetLinker interface. // MockNetLinker is a mock of NetLinker interface.
type MockNetLinker struct { type MockNetLinker struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockNetLinkerMockRecorder recorder *MockNetLinkerMockRecorder
isgomock struct{}
} }
// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker. // MockNetLinkerMockRecorder is the mock recorder for MockNetLinker.
@@ -36,91 +42,91 @@ func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder {
} }
// AddrList mocks base method. // AddrList mocks base method.
func (m *MockNetLinker) AddrList(arg0 uint32, arg1 byte) ([]netip.Prefix, error) { func (m *MockNetLinker) AddrList(linkIndex uint32, family uint8) ([]netip.Prefix, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrList", arg0, arg1) ret := m.ctrl.Call(m, "AddrList", linkIndex, family)
ret0, _ := ret[0].([]netip.Prefix) ret0, _ := ret[0].([]netip.Prefix)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// AddrList indicates an expected call of AddrList. // AddrList indicates an expected call of AddrList.
func (mr *MockNetLinkerMockRecorder) AddrList(arg0, arg1 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) AddrList(linkIndex, family any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrList", reflect.TypeOf((*MockNetLinker)(nil).AddrList), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrList", reflect.TypeOf((*MockNetLinker)(nil).AddrList), linkIndex, family)
} }
// AddrReplace mocks base method. // AddrReplace mocks base method.
func (m *MockNetLinker) AddrReplace(arg0 uint32, arg1 netip.Prefix) error { func (m *MockNetLinker) AddrReplace(linkIndex uint32, prefix netip.Prefix) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrReplace", arg0, arg1) ret := m.ctrl.Call(m, "AddrReplace", linkIndex, prefix)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// AddrReplace indicates an expected call of AddrReplace. // AddrReplace indicates an expected call of AddrReplace.
func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) AddrReplace(linkIndex, prefix any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), linkIndex, prefix)
} }
// LinkAdd mocks base method. // LinkAdd mocks base method.
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) (uint32, error) { func (m *MockNetLinker) LinkAdd(link netlink.Link) (uint32, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkAdd", arg0) ret := m.ctrl.Call(m, "LinkAdd", link)
ret0, _ := ret[0].(uint32) ret0, _ := ret[0].(uint32)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkAdd indicates an expected call of LinkAdd. // LinkAdd indicates an expected call of LinkAdd.
func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkAdd(link any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), link)
} }
// LinkByIndex mocks base method. // LinkByIndex mocks base method.
func (m *MockNetLinker) LinkByIndex(arg0 uint32) (netlink.Link, error) { func (m *MockNetLinker) LinkByIndex(index uint32) (netlink.Link, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByIndex", arg0) ret := m.ctrl.Call(m, "LinkByIndex", index)
ret0, _ := ret[0].(netlink.Link) ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkByIndex indicates an expected call of LinkByIndex. // LinkByIndex indicates an expected call of LinkByIndex.
func (mr *MockNetLinkerMockRecorder) LinkByIndex(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkByIndex(index any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByIndex", reflect.TypeOf((*MockNetLinker)(nil).LinkByIndex), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByIndex", reflect.TypeOf((*MockNetLinker)(nil).LinkByIndex), index)
} }
// LinkByName mocks base method. // LinkByName mocks base method.
func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) { func (m *MockNetLinker) LinkByName(name string) (netlink.Link, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByName", arg0) ret := m.ctrl.Call(m, "LinkByName", name)
ret0, _ := ret[0].(netlink.Link) ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkByName indicates an expected call of LinkByName. // LinkByName indicates an expected call of LinkByName.
func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkByName(name any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), name)
} }
// LinkDel mocks base method. // LinkDel mocks base method.
func (m *MockNetLinker) LinkDel(arg0 uint32) error { func (m *MockNetLinker) LinkDel(index uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkDel", arg0) ret := m.ctrl.Call(m, "LinkDel", index)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkDel indicates an expected call of LinkDel. // LinkDel indicates an expected call of LinkDel.
func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkDel(index any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), index)
} }
// LinkList mocks base method. // LinkList mocks base method.
@@ -139,129 +145,129 @@ func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call {
} }
// LinkSetDown mocks base method. // LinkSetDown mocks base method.
func (m *MockNetLinker) LinkSetDown(arg0 uint32) error { func (m *MockNetLinker) LinkSetDown(index uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetDown", arg0) ret := m.ctrl.Call(m, "LinkSetDown", index)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetDown indicates an expected call of LinkSetDown. // LinkSetDown indicates an expected call of LinkSetDown.
func (mr *MockNetLinkerMockRecorder) LinkSetDown(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetDown(index any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), index)
} }
// LinkSetUp mocks base method. // LinkSetUp mocks base method.
func (m *MockNetLinker) LinkSetUp(arg0 uint32) error { func (m *MockNetLinker) LinkSetUp(index uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetUp", arg0) ret := m.ctrl.Call(m, "LinkSetUp", index)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetUp indicates an expected call of LinkSetUp. // LinkSetUp indicates an expected call of LinkSetUp.
func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetUp(index any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), index)
} }
// RouteAdd mocks base method. // RouteAdd mocks base method.
func (m *MockNetLinker) RouteAdd(arg0 netlink.Route) error { func (m *MockNetLinker) RouteAdd(route netlink.Route) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteAdd", arg0) ret := m.ctrl.Call(m, "RouteAdd", route)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RouteAdd indicates an expected call of RouteAdd. // RouteAdd indicates an expected call of RouteAdd.
func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteAdd(route any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), route)
} }
// RouteDel mocks base method. // RouteDel mocks base method.
func (m *MockNetLinker) RouteDel(arg0 netlink.Route) error { func (m *MockNetLinker) RouteDel(route netlink.Route) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteDel", arg0) ret := m.ctrl.Call(m, "RouteDel", route)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RouteDel indicates an expected call of RouteDel. // RouteDel indicates an expected call of RouteDel.
func (mr *MockNetLinkerMockRecorder) RouteDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteDel(route any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteDel", reflect.TypeOf((*MockNetLinker)(nil).RouteDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteDel", reflect.TypeOf((*MockNetLinker)(nil).RouteDel), route)
} }
// RouteList mocks base method. // RouteList mocks base method.
func (m *MockNetLinker) RouteList(arg0 byte) ([]netlink.Route, error) { func (m *MockNetLinker) RouteList(family uint8) ([]netlink.Route, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteList", arg0) ret := m.ctrl.Call(m, "RouteList", family)
ret0, _ := ret[0].([]netlink.Route) ret0, _ := ret[0].([]netlink.Route)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// RouteList indicates an expected call of RouteList. // RouteList indicates an expected call of RouteList.
func (mr *MockNetLinkerMockRecorder) RouteList(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteList(family any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), family)
} }
// RouteReplace mocks base method. // RouteReplace mocks base method.
func (m *MockNetLinker) RouteReplace(arg0 netlink.Route) error { func (m *MockNetLinker) RouteReplace(route netlink.Route) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteReplace", arg0) ret := m.ctrl.Call(m, "RouteReplace", route)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RouteReplace indicates an expected call of RouteReplace. // RouteReplace indicates an expected call of RouteReplace.
func (mr *MockNetLinkerMockRecorder) RouteReplace(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteReplace(route any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteReplace", reflect.TypeOf((*MockNetLinker)(nil).RouteReplace), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteReplace", reflect.TypeOf((*MockNetLinker)(nil).RouteReplace), route)
} }
// RuleAdd mocks base method. // RuleAdd mocks base method.
func (m *MockNetLinker) RuleAdd(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleAdd(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleAdd", arg0) ret := m.ctrl.Call(m, "RuleAdd", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleAdd indicates an expected call of RuleAdd. // RuleAdd indicates an expected call of RuleAdd.
func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleAdd(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), rule)
} }
// RuleDel mocks base method. // RuleDel mocks base method.
func (m *MockNetLinker) RuleDel(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleDel(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleDel", arg0) ret := m.ctrl.Call(m, "RuleDel", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleDel indicates an expected call of RuleDel. // RuleDel indicates an expected call of RuleDel.
func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleDel(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), rule)
} }
// RuleList mocks base method. // RuleList mocks base method.
func (m *MockNetLinker) RuleList(arg0 byte) ([]netlink.Rule, error) { func (m *MockNetLinker) RuleList(family uint8) ([]netlink.Rule, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleList", arg0) ret := m.ctrl.Call(m, "RuleList", family)
ret0, _ := ret[0].([]netlink.Rule) ret0, _ := ret[0].([]netlink.Rule)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// RuleList indicates an expected call of RuleList. // RuleList indicates an expected call of RuleList.
func (mr *MockNetLinkerMockRecorder) RuleList(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleList(family any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleList", reflect.TypeOf((*MockNetLinker)(nil).RuleList), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleList", reflect.TypeOf((*MockNetLinker)(nil).RuleList), family)
} }
+1 -1
View File
@@ -5,10 +5,10 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/netlink" "github.com/qdm12/gluetun/internal/netlink"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func makeNetipPrefix(n byte) netip.Prefix { func makeNetipPrefix(n byte) netip.Prefix {
@@ -8,9 +8,9 @@ import (
"net/url" "net/url"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_authHandler_ServeHTTP(t *testing.T) { func Test_authHandler_ServeHTTP(t *testing.T) {
+17 -11
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/server/middlewares/auth (interfaces: DebugLogger) // Source: github.com/qdm12/gluetun/internal/server/middlewares/auth (interfaces: DebugLogger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=auth . DebugLogger
//
// Package auth is a generated GoMock package. // Package auth is a generated GoMock package.
package auth package auth
@@ -7,13 +12,14 @@ package auth
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockDebugLogger is a mock of DebugLogger interface. // MockDebugLogger is a mock of DebugLogger interface.
type MockDebugLogger struct { type MockDebugLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockDebugLoggerMockRecorder recorder *MockDebugLoggerMockRecorder
isgomock struct{}
} }
// MockDebugLoggerMockRecorder is the mock recorder for MockDebugLogger. // MockDebugLoggerMockRecorder is the mock recorder for MockDebugLogger.
@@ -34,35 +40,35 @@ func (m *MockDebugLogger) EXPECT() *MockDebugLoggerMockRecorder {
} }
// Debugf mocks base method. // Debugf mocks base method.
func (m *MockDebugLogger) Debugf(arg0 string, arg1 ...interface{}) { func (m *MockDebugLogger) Debugf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Debugf", varargs...) m.ctrl.Call(m, "Debugf", varargs...)
} }
// Debugf indicates an expected call of Debugf. // Debugf indicates an expected call of Debugf.
func (mr *MockDebugLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockDebugLoggerMockRecorder) Debugf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockDebugLogger)(nil).Debugf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockDebugLogger)(nil).Debugf), varargs...)
} }
// Warnf mocks base method. // Warnf mocks base method.
func (m *MockDebugLogger) Warnf(arg0 string, arg1 ...interface{}) { func (m *MockDebugLogger) Warnf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Warnf", varargs...) m.ctrl.Call(m, "Warnf", varargs...)
} }
// Warnf indicates an expected call of Warnf. // Warnf indicates an expected call of Warnf.
func (mr *MockDebugLoggerMockRecorder) Warnf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockDebugLoggerMockRecorder) Warnf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockDebugLogger)(nil).Warnf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockDebugLogger)(nil).Warnf), varargs...)
} }
-3
View File
@@ -43,7 +43,6 @@ type cmdType byte
const ( const (
connect cmdType = 1 connect cmdType = 1
bind cmdType = 2
udpAssociate cmdType = 3 udpAssociate cmdType = 3
) )
@@ -51,8 +50,6 @@ func (c cmdType) String() string {
switch c { switch c {
case connect: case connect:
return "connect" return "connect"
case bind:
return "bind"
case udpAssociate: case udpAssociate:
return "UDP associate" return "UDP associate"
default: default:
+19 -13
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/socks5 (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/socks5 (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package=socks5 . Logger
//
// Package socks5 is a generated GoMock package. // Package socks5 is a generated GoMock package.
package socks5 package socks5
@@ -7,13 +12,14 @@ package socks5
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,35 +40,35 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Infof mocks base method. // Infof mocks base method.
func (m *MockLogger) Infof(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Infof(format string, a ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a_2 := range a {
varargs = append(varargs, a) varargs = append(varargs, a_2)
} }
m.ctrl.Call(m, "Infof", varargs...) m.ctrl.Call(m, "Infof", varargs...)
} }
// Infof indicates an expected call of Infof. // Infof indicates an expected call of Infof.
func (mr *MockLoggerMockRecorder) Infof(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Infof(format any, a ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, a...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLogger)(nil).Infof), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLogger)(nil).Infof), varargs...)
} }
// Warnf mocks base method. // Warnf mocks base method.
func (m *MockLogger) Warnf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Warnf(format string, a ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a_2 := range a {
varargs = append(varargs, a) varargs = append(varargs, a_2)
} }
m.ctrl.Call(m, "Warnf", varargs...) m.ctrl.Call(m, "Warnf", varargs...)
} }
// Warnf indicates an expected call of Warnf. // Warnf indicates an expected call of Warnf.
func (mr *MockLoggerMockRecorder) Warnf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warnf(format any, a ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, a...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockLogger)(nil).Warnf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warnf", reflect.TypeOf((*MockLogger)(nil).Warnf), varargs...)
} }
+1 -1
View File
@@ -10,7 +10,7 @@ import (
) )
// See https://datatracker.ietf.org/doc/html/rfc1928#section-6 // See https://datatracker.ietf.org/doc/html/rfc1928#section-6
func (c *socksConn) encodeFailedResponse(writer io.Writer, socksVersion byte, reply replyCode) { //nolint:unparam func (c *socksConn) encodeFailedResponse(writer io.Writer, socksVersion byte, reply replyCode) {
_, err := writer.Write([]byte{ _, err := writer.Write([]byte{
socksVersion, socksVersion,
byte(reply), byte(reply),
+89 -49
View File
@@ -2,6 +2,7 @@ package socks5
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net" "net"
"sync" "sync"
@@ -15,12 +16,13 @@ type server struct {
logger Logger logger Logger
// internal fields // internal fields
listener net.Listener tcpListener net.Listener
udpRouter *udpRouter
listening atomic.Bool listening atomic.Bool
socksConnCtx context.Context //nolint:containedctx socksConnCtx context.Context //nolint:containedctx
socksConnCancel context.CancelFunc socksConnCancel context.CancelFunc
done <-chan struct{} done <-chan error
stopping atomic.Bool stopCh chan<- struct{}
} }
func newServer(settings Settings) *server { func newServer(settings Settings) *server {
@@ -39,19 +41,28 @@ func (s *server) String() string {
func (s *server) Start(ctx context.Context) (runErr <-chan error, err error) { func (s *server) Start(ctx context.Context) (runErr <-chan error, err error) {
s.socksConnCtx, s.socksConnCancel = context.WithCancel(context.Background()) s.socksConnCtx, s.socksConnCancel = context.WithCancel(context.Background())
config := &net.ListenConfig{} config := &net.ListenConfig{}
s.listener, err = config.Listen(ctx, "tcp", s.address) s.tcpListener, err = config.Listen(ctx, "tcp", s.address)
if err != nil { if err != nil {
return nil, fmt.Errorf("listening on %s: %w", s.address, err) return nil, fmt.Errorf("TCP listening on %s: %w", s.address, err)
}
s.udpRouter, err = newUDPRouter(ctx, s.address, s.logger)
if err != nil {
_ = s.tcpListener.Close()
return nil, fmt.Errorf("creating UDP router: %w", err)
} }
s.listening.Store(true) s.listening.Store(true)
s.logger.Infof("SOCKS5 server listening on %s", s.listener.Addr()) s.logger.Infof("SOCKS5 TCP server listening on %s", s.tcpListener.Addr())
s.logger.Infof("SOCKS5 UDP server listening on %s", s.udpRouter.localAddress())
ready := make(chan struct{}) ready := make(chan struct{})
runErrCh := make(chan error) runErrCh := make(chan error)
runErr = runErrCh runErr = runErrCh
done := make(chan struct{}) done := make(chan error)
s.done = done s.done = done
go s.runServer(ready, runErrCh, done) stop := make(chan struct{})
s.stopCh = stop
go s.runServer(ready, runErrCh, stop, done)
select { select {
case <-ready: case <-ready:
case <-ctx.Done(): case <-ctx.Done():
@@ -62,61 +73,90 @@ func (s *server) Start(ctx context.Context) (runErr <-chan error, err error) {
} }
func (s *server) runServer(ready chan<- struct{}, func (s *server) runServer(ready chan<- struct{},
runErrCh chan<- error, done chan<- struct{}, runErrCh chan<- error, stop <-chan struct{}, done chan<- error,
) { ) {
close(ready) close(ready)
defer close(done) defer close(done)
wg := new(sync.WaitGroup)
defer wg.Wait()
dialer := &net.Dialer{} udpErrCh := make(chan error)
for { go func() {
connection, err := s.listener.Accept() udpErrCh <- s.udpRouter.run(s.socksConnCtx)
if err != nil { }()
if !s.stopping.Load() {
_ = s.stop() tcpErrCh := make(chan error)
runErrCh <- fmt.Errorf("accepting connection: %w", err) go func() {
} var wg sync.WaitGroup
return defer wg.Wait()
}
wg.Add(1) dialer := &net.Dialer{}
go func(ctx context.Context, connection net.Conn, for {
dialer *net.Dialer, wg *sync.WaitGroup, connection, err := s.tcpListener.Accept()
) {
defer wg.Done()
socksConn := &socksConn{
dialer: dialer,
username: s.username,
password: s.password,
clientConn: connection,
logger: s.logger,
}
err := socksConn.run(ctx)
if err != nil { if err != nil {
s.logger.Infof("running socks connection: %s", err) s.socksConnCancel() // stop ongoing TCP socks connections - no impact on UDP
tcpErrCh <- fmt.Errorf("accepting connection: %w", err)
return
} }
}(s.socksConnCtx, connection, dialer, wg) wg.Go(func() {
connection := connection // capture loop variable
socksConn := &socksConn{
dialer: dialer,
username: s.username,
password: s.password,
clientConn: connection,
udpRouter: s.udpRouter,
logger: s.logger,
}
err := socksConn.run(s.socksConnCtx)
if err != nil {
s.logger.Infof("running socks connection: %s", err)
}
})
}
}()
select {
case <-stop:
s.listening.Store(false)
var errs []error
err := s.tcpListener.Close()
if err != nil {
errs = append(errs, fmt.Errorf("closing TCP listener: %w", err))
}
// stop ongoing TCP socks connections. This impacts the udpRouter run error when it is being closed.
s.socksConnCancel()
<-tcpErrCh // wait for TCP server to stop
err = s.udpRouter.close()
if err != nil {
errs = append(errs, fmt.Errorf("closing UDP router: %w", err))
}
<-udpErrCh // wait for UDP router to stop
if len(errs) > 0 {
// Only write to the done channel if the [server.Stop] method is waiting to read from it
done <- errors.Join(errs...)
}
// If no error, the done channel is closed so the error is effectively `nil`
// Note: do NOT write an error the runError channel, since we are stopping the server gracefully.
case err := <-udpErrCh:
_ = s.tcpListener.Close() // stop accepting new TCP connections
s.socksConnCancel() // stop ongoing TCP socks connections
<-tcpErrCh // wait for TCP server to stop
runErrCh <- fmt.Errorf("running UDP router: %w", err)
case err := <-tcpErrCh:
s.socksConnCancel()
_ = s.udpRouter.close() // stop UDP router
<-udpErrCh // wait for UDP router to stop
runErrCh <- fmt.Errorf("running TCP server: %w", err)
} }
} }
func (s *server) Stop() (err error) { func (s *server) Stop() (err error) {
s.stopping.Store(true) close(s.stopCh)
err = s.stop() return <-s.done
<-s.done // wait for run goroutine to finish
s.stopping.Store(false)
return err
}
func (s *server) stop() error {
s.listening.Store(false)
err := s.listener.Close()
s.socksConnCancel() // stop ongoing socks connections
return err
} }
func (s *server) listeningAddress() net.Addr { func (s *server) listeningAddress() net.Addr {
if s.listening.Load() { if s.listening.Load() {
return s.listener.Addr() return s.tcpListener.Addr()
} }
return nil return nil
} }
+112
View File
@@ -0,0 +1,112 @@
//go:build integration
package socks5
import (
"math/rand/v2"
"net"
"testing"
"time"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Server_UDPResolution(t *testing.T) {
t.Parallel()
ctx := t.Context()
server := newServer(Settings{
Address: "127.0.0.1:0",
Logger: noopLogger{},
})
runErr, err := server.Start(ctx)
require.NoError(t, err, "starting SOCKS5 server")
const timeout = 3 * time.Second
// Connect to the SOCKS5 server via TCP to negotiate UDP associate
dialer := &net.Dialer{Timeout: timeout}
tcpConn, err := dialer.DialContext(ctx, "tcp", server.listeningAddress().String())
require.NoError(t, err, "tcp connecting to SOCKS5 server")
t.Cleanup(func() { tcpConn.Close() })
negotiateSOCKS5(t, tcpConn, "", "")
// UDP Associate Command: [VERSION (5), CMD (3 = UDP ASSOC), RSV (0), ATYP (1 = IPv4), ADDR (0.0.0.0), PORT (0)]
_, err = tcpConn.Write([]byte{5, 3, 0, 1, 0, 0, 0, 0, 0, 0})
require.NoError(t, err, "sending UDP ASSOC request")
relayAddressString, err := readSOCKS5ResponseAddress(t, tcpConn)
require.NoError(t, err, "reading UDP ASSOC reply")
relayAddress, err := net.ResolveUDPAddr("udp", relayAddressString)
require.NoError(t, err, "resolving udp relay address")
// Dial the relay using IPv4 so source IP family matches the control connection.
udpConn, err := net.DialUDP("udp4", nil, relayAddress)
require.NoError(t, err, "dialing UDP relay")
t.Cleanup(func() { _ = udpConn.Close() })
queryID := uint16(rand.Uint32()) //nolint:gosec
dnsRequest := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: queryID,
RecursionDesired: true,
},
Question: []dns.Question{{
Name: dns.Fqdn("github.com"),
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}},
}
dnsQuery, err := dnsRequest.Pack()
require.NoError(t, err)
// Encapsulate DNS payload into SOCKS5 UDP Request Header
// [RSV (0,0), FRAG (0), ATYP (1 = IPv4), DST.ADDR (1.1.1.1), DST.PORT (53)]
packet := append([]byte{0, 0, 0, 1, 1, 1, 1, 1, 0, 53}, dnsQuery...)
// Send encapsulated packet to the proxy's UDP relay address
_, err = udpConn.Write(packet)
require.NoError(t, err, "sending UDP packet to relay")
// Read response from the proxy relay
err = udpConn.SetReadDeadline(time.Now().Add(timeout))
require.NoError(t, err, "setting read deadline on UDP connection")
buffer := make([]byte, 2048)
n, err := udpConn.Read(buffer)
require.NoError(t, err, "receiving UDP response from relay")
const minimumHeaderSize = 10
require.GreaterOrEqual(t, n, minimumHeaderSize, "received UDP packet too short to contain valid SOCKS5 header")
// Verify header layout and slice out the raw DNS response
// Header format: RSV(2) FRAG(1) ATYP(1) DST.ADDR(variable) DST.PORT(2)
atyp := buffer[3]
var headerSize int
switch atyp {
case 1: // IPv4
headerSize = 10
case 3: // Domain name
headerSize = 4 + 1 + int(buffer[4]) + 2
case 4: // IPv6
headerSize = 22
default:
t.Fatalf("Unknown ATYP in SOCKS5 UDP header: %d", atyp)
}
dnsResponse := new(dns.Msg)
err = dnsResponse.Unpack(buffer[headerSize:n])
require.NoError(t, err, "unpacking DNS response from SOCKS5 UDP packet")
assert.Equal(t, queryID, dnsResponse.Id, "DNS response ID should match query ID")
select {
case err := <-runErr:
require.NoError(t, err, "SOCKS5 server run error")
default:
}
err = server.Stop()
require.NoError(t, err, "stopping SOCKS5 server")
}
+249 -1
View File
@@ -10,6 +10,7 @@ import (
"net/netip" "net/netip"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
var ( var (
@@ -23,6 +24,7 @@ type socksConn struct {
username string username string
password string password string
clientConn net.Conn clientConn net.Conn
udpRouter *udpRouter
logger Logger logger Logger
} }
@@ -109,11 +111,29 @@ func (c *socksConn) handleRequest(ctx context.Context) error {
c.encodeFailedResponse(c.clientConn, socksVersion, generalServerFailure) c.encodeFailedResponse(c.clientConn, socksVersion, generalServerFailure)
return err return err
} }
if request.command != connect {
switch request.command {
case connect:
err = c.handleConnectRequest(ctx, socksVersion, request)
if err != nil {
return fmt.Errorf("handling %s request: %w", request.command, err)
}
return nil
case udpAssociate:
err = c.handleUDPAssociateRequest(ctx, socksVersion, request)
if err != nil {
return fmt.Errorf("handling %s request: %w", request.command, err)
}
return nil
default:
c.encodeFailedResponse(c.clientConn, socksVersion, commandNotSupported) c.encodeFailedResponse(c.clientConn, socksVersion, commandNotSupported)
return fmt.Errorf("command %s is not supported", request.command) return fmt.Errorf("command %s is not supported", request.command)
} }
}
func (c *socksConn) handleConnectRequest(ctx context.Context,
socksVersion byte, request request,
) error {
destinationAddress := net.JoinHostPort(request.destination, fmt.Sprint(request.port)) destinationAddress := net.JoinHostPort(request.destination, fmt.Sprint(request.port))
destinationConn, err := c.dialer.DialContext(ctx, "tcp", destinationAddress) destinationConn, err := c.dialer.DialContext(ctx, "tcp", destinationAddress)
if err != nil { if err != nil {
@@ -176,6 +196,234 @@ func (c *socksConn) handleRequest(ctx context.Context) error {
} }
} }
func (c *socksConn) handleUDPAssociateRequest(ctx context.Context,
socksVersion byte, request request,
) error {
expectedAddrPort, err := udpAssociateExpectedClientEndpoint(request)
if err != nil {
c.encodeFailedResponse(c.clientConn, socksVersion, addressTypeNotSupported)
return fmt.Errorf("deriving expected client address and port from request: %w", err)
}
bindAddress, bindPort, bindAddrType, err := c.udpAssociationAddresses()
if err != nil {
c.encodeFailedResponse(c.clientConn, socksVersion, generalServerFailure)
return fmt.Errorf("getting udp association addresses: %w", err)
}
association, err := c.udpRouter.registerAssociation(c.clientConn, expectedAddrPort)
if err != nil {
c.encodeFailedResponse(c.clientConn, socksVersion, generalServerFailure)
return fmt.Errorf("registering udp association: %w", err)
}
defer c.udpRouter.unregisterAssociation(association)
err = c.encodeSuccessResponse(c.clientConn, socksVersion, succeeded,
bindAddrType, bindAddress, bindPort)
if err != nil {
c.encodeFailedResponse(c.clientConn, socksVersion, generalServerFailure)
return fmt.Errorf("writing successful %s response: %w", udpAssociate, err)
}
associationCtx, associationCancel := context.WithCancel(ctx)
defer associationCancel()
var wg sync.WaitGroup
wg.Go(func() {
c.udpRouter.runAssociationHandler(associationCtx, association)
})
wg.Go(func() {
_, _ = io.Copy(io.Discard, c.clientConn)
associationCancel()
})
<-associationCtx.Done()
wg.Wait()
return nil
}
func udpAssociateExpectedClientEndpoint(request request) (expectedAddrPort netip.AddrPort, err error) {
switch request.addressType {
case ipv4, ipv6:
expectedClientAddress, parseErr := netip.ParseAddr(request.destination)
if parseErr != nil {
return netip.AddrPort{}, fmt.Errorf("parsing destination address: %w", parseErr)
}
expectedClientAddress = expectedClientAddress.Unmap()
if !expectedClientAddress.IsUnspecified() {
return netip.AddrPortFrom(expectedClientAddress, request.port), nil
}
return netip.AddrPortFrom(netip.Addr{}, request.port), nil
case domainName:
// For UDP associate, client endpoint matching is based on observed UDP source
// address/port. A hostname is not directly matchable at this stage, so we
// ignore the domain name request destination entirely.
return netip.AddrPortFrom(netip.Addr{}, request.port), nil
default:
return netip.AddrPort{}, fmt.Errorf("address type %d is not supported", request.addressType)
}
}
func (c *socksConn) udpAssociationAddresses() (bindAddress string,
bindPort uint16, bindAddrType addrType, err error,
) {
localAddress := c.udpRouter.localAddress().String()
host, portString, err := net.SplitHostPort(localAddress)
if err != nil {
return "", 0, 0, fmt.Errorf("splitting local address: %w", err)
}
port, err := strconv.ParseUint(portString, 10, 16)
if err != nil {
return "", 0, 0, fmt.Errorf("parsing local port: %w", err)
}
bindAddress = host
bindPort = uint16(port)
if isUnspecifiedIPAddress(bindAddress) {
controlLocalAddress := c.clientConn.LocalAddr().String()
controlLocalHost, _, splitErr := net.SplitHostPort(controlLocalAddress)
if splitErr != nil {
return "", 0, 0, fmt.Errorf("splitting control connection local address: %w", splitErr)
}
bindAddress = controlLocalHost
}
ipAddress := net.ParseIP(bindAddress)
if ipAddress == nil {
bindAddrType = domainName
return bindAddress, bindPort, bindAddrType, nil
}
if ipAddress.To4() != nil {
bindAddrType = ipv4
} else {
bindAddrType = ipv6
}
return bindAddress, bindPort, bindAddrType, nil
}
func isUnspecifiedIPAddress(address string) bool {
ipAddress, err := netip.ParseAddr(address)
if err != nil {
return false
}
return ipAddress.IsUnspecified()
}
func decodeUDPDatagram(packet []byte) (destination string, payload []byte, err error) {
const minimumPacketLength = 4
if len(packet) < minimumPacketLength {
return "", nil, fmt.Errorf("packet is too short: %d", len(packet))
}
if packet[0] != 0 || packet[1] != 0 {
return "", nil, fmt.Errorf("reserved bytes are invalid: %x %x", packet[0], packet[1])
}
if packet[2] != 0 {
return "", nil, fmt.Errorf("fragmentation is not supported")
}
offset := 3
addressType := addrType(packet[offset])
offset++
switch addressType {
case ipv4:
const ipv4Length = 4
if len(packet) < offset+ipv4Length+2 {
return "", nil, fmt.Errorf("packet is too short for IPv4 address")
}
var ip [ipv4Length]byte
copy(ip[:], packet[offset:offset+ipv4Length])
destination = netip.AddrFrom4(ip).String()
offset += ipv4Length
case ipv6:
const ipv6Length = 16
if len(packet) < offset+ipv6Length+2 {
return "", nil, fmt.Errorf("packet is too short for IPv6 address")
}
var ip [ipv6Length]byte
copy(ip[:], packet[offset:offset+ipv6Length])
destination = netip.AddrFrom16(ip).String()
offset += ipv6Length
case domainName:
if len(packet) < offset+1 {
return "", nil, fmt.Errorf("packet is too short for domain name length")
}
domainNameLength := int(packet[offset])
offset++
if len(packet) < offset+domainNameLength+2 {
return "", nil, fmt.Errorf("packet is too short for domain name")
}
destination = string(packet[offset : offset+domainNameLength])
offset += domainNameLength
default:
return "", nil, fmt.Errorf("address type is not supported: %d", addressType)
}
port := binary.BigEndian.Uint16(packet[offset : offset+2])
destination = net.JoinHostPort(destination, fmt.Sprint(port))
offset += 2
payload = packet[offset:]
return destination, payload, nil
}
func encodeUDPDatagramToBuffer(writer io.Writer, sourceAddrPort netip.AddrPort,
payload []byte,
) error {
address := sourceAddrPort.Addr()
if !address.IsValid() {
return errors.New("source address is not valid")
}
err := writeUDPDatagramSourceAddress(writer, address)
if err != nil {
return fmt.Errorf("writing source address: %w", err)
}
var portBytes [2]byte
binary.BigEndian.PutUint16(portBytes[:], sourceAddrPort.Port())
_, err = writer.Write(portBytes[:])
if err != nil {
return fmt.Errorf("writing destination port: %w", err)
}
_, err = writer.Write(payload)
if err != nil {
return fmt.Errorf("writing payload: %w", err)
}
return nil
}
func writeUDPDatagramSourceAddress(writer io.Writer, address netip.Addr) error {
var addrType addrType
var addressBytes []byte
switch {
case address.Is4():
addrType = ipv4
array := address.As4()
addressBytes = array[:]
case address.Is6():
addrType = ipv6
array := address.As16()
addressBytes = array[:]
default:
return fmt.Errorf("address type is not supported: %v", address)
}
_, err := writer.Write([]byte{0, 0, 0, byte(addrType)})
if err != nil {
return fmt.Errorf("writing header: %w", err)
}
_, err = writer.Write(addressBytes)
if err != nil {
return fmt.Errorf("writing IP address: %w", err)
}
return nil
}
// See https://datatracker.ietf.org/doc/html/rfc1928#section-3 // See https://datatracker.ietf.org/doc/html/rfc1928#section-3
func verifyFirstNegotiation(reader io.Reader, requiredMethod authMethod) error { func verifyFirstNegotiation(reader io.Reader, requiredMethod authMethod) error {
const headerLength = 2 // version + nMethods bytes const headerLength = 2 // version + nMethods bytes
+503 -39
View File
@@ -2,16 +2,20 @@ package socks5
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"errors"
"fmt"
"io" "io"
"net" "net"
"net/netip"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
type noopLogger struct{} type noopLogger struct{}
@@ -96,6 +100,178 @@ func TestServerProxy(t *testing.T) {
} }
} }
func TestServerProxyTCPAndUDPParallel(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
username string
password string
}{
"no_auth": {},
"with_auth": {
username: "user",
password: "pass",
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
backendTCPListener, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", "127.0.0.1:0")
require.NoError(t, err)
backendTCPConnChannel := make(chan net.Conn, 1)
go func() {
connection, err := backendTCPListener.Accept()
if err != nil {
return
}
backendTCPConnChannel <- connection
}()
backendUDPPacketConn, err := (&net.ListenConfig{}).ListenPacket(t.Context(), "udp", "127.0.0.1:0")
require.NoError(t, err)
server := newServer(Settings{
Username: testCase.username,
Password: testCase.password,
Address: "127.0.0.1:0",
Logger: noopLogger{},
})
_, err = server.Start(t.Context())
require.NoError(t, err)
t.Cleanup(func() {
_ = server.Stop()
_ = backendTCPListener.Close()
_ = backendUDPPacketConn.Close()
})
clientTCPConn := dialSOCKS5(t, server.listeningAddress().String(),
backendTCPListener.Addr().String(), testCase.username, testCase.password)
defer clientTCPConn.Close()
backendTCPConn := <-backendTCPConnChannel
defer backendTCPConn.Close()
udpControlConn, clientUDPConn := dialSOCKS5UDPAssociate(t,
server.listeningAddress().String(), testCase.username, testCase.password)
defer udpControlConn.Close()
defer clientUDPConn.Close()
tcpErrCh := make(chan error, 1)
go func() {
tcpErrCh <- runTCPProxyRoundTrip(clientTCPConn, backendTCPConn)
}()
udpErrCh := make(chan error, 1)
go func() {
udpErrCh <- runUDPProxyRoundTrip(t.Context(), clientUDPConn, backendUDPPacketConn)
}()
err = <-tcpErrCh
require.NoError(t, err)
err = <-udpErrCh
require.NoError(t, err)
})
}
}
func runTCPProxyRoundTrip(clientTCPConn net.Conn, backendTCPConn net.Conn) error {
clientMessage := []byte("hello from client")
_, err := clientTCPConn.Write(clientMessage)
if err != nil {
return err
}
received := make([]byte, len(clientMessage))
_, err = io.ReadFull(backendTCPConn, received)
if err != nil {
return err
}
if !bytes.Equal(clientMessage, received) {
return errors.New("backend did not receive expected TCP payload")
}
backendMessage := []byte("hello from backend")
_, err = backendTCPConn.Write(backendMessage)
if err != nil {
return err
}
receivedByClient := make([]byte, len(backendMessage))
_, err = io.ReadFull(clientTCPConn, receivedByClient)
if err != nil {
return err
}
if !bytes.Equal(backendMessage, receivedByClient) {
return errors.New("client did not receive expected TCP payload")
}
return nil
}
func runUDPProxyRoundTrip(ctx context.Context, clientUDPConn *net.UDPConn, backendUDPPacketConn net.PacketConn) error {
udpPayload := []byte("hello from udp client")
udpRequest, err := makeSOCKS5UDPDatagram(backendUDPPacketConn.LocalAddr().String(), udpPayload)
if err != nil {
return err
}
_, err = clientUDPConn.Write(udpRequest)
if err != nil {
return err
}
deadline, hasDeadline := ctx.Deadline()
if hasDeadline {
err = backendUDPPacketConn.SetReadDeadline(deadline)
if err != nil {
return fmt.Errorf("setting read deadline on backend connection: %w", err)
}
}
const bufferSize = 512
backendReadBuffer := make([]byte, bufferSize)
packetLength, proxyAddress, err := backendUDPPacketConn.ReadFrom(backendReadBuffer)
if err != nil {
return err
}
if !bytes.Equal(udpPayload, backendReadBuffer[:packetLength]) {
return errors.New("backend did not receive expected UDP payload")
}
backendUDPReply := []byte("hello from udp backend")
_, err = backendUDPPacketConn.WriteTo(backendUDPReply, proxyAddress)
if err != nil {
return err
}
if hasDeadline {
err = clientUDPConn.SetReadDeadline(deadline)
if err != nil {
return fmt.Errorf("setting read deadline on client connection: %w", err)
}
}
udpResponseBuffer := make([]byte, 1024)
responseLength, err := clientUDPConn.Read(udpResponseBuffer)
if err != nil {
return err
}
destinationAddress, udpResponsePayload, err := parseSOCKS5UDPDatagram(udpResponseBuffer[:responseLength])
if err != nil {
return err
}
if !bytes.Equal(backendUDPReply, udpResponsePayload) {
return errors.New("client did not receive expected UDP payload")
}
if destinationAddress != backendUDPPacketConn.LocalAddr().String() {
return errors.New("udp response destination address mismatch")
}
return nil
}
// dialSOCKS5 performs the full SOCKS5 handshake (with optional username/password // dialSOCKS5 performs the full SOCKS5 handshake (with optional username/password
// subnegotiation) and returns a connected net.Conn ready for data exchange. // subnegotiation) and returns a connected net.Conn ready for data exchange.
func dialSOCKS5(t *testing.T, proxyAddr, targetAddr, username, password string) net.Conn { func dialSOCKS5(t *testing.T, proxyAddr, targetAddr, username, password string) net.Conn {
@@ -109,6 +285,55 @@ func dialSOCKS5(t *testing.T, proxyAddr, targetAddr, username, password string)
conn, err := (&net.Dialer{}).DialContext(t.Context(), "tcp", proxyAddr) conn, err := (&net.Dialer{}).DialContext(t.Context(), "tcp", proxyAddr)
require.NoError(t, err) require.NoError(t, err)
negotiateSOCKS5(t, conn, username, password)
var connectRequest []byte
if ip := net.ParseIP(host).To4(); ip != nil {
connectRequest = []byte{socks5Version, byte(connect), 0, byte(ipv4)}
connectRequest = append(connectRequest, ip...)
} else {
connectRequest = []byte{socks5Version, byte(connect), 0, byte(domainName), byte(len(host))}
connectRequest = append(connectRequest, []byte(host)...)
}
connectRequest = binary.BigEndian.AppendUint16(connectRequest, uint16(targetPort)) //nolint:gosec
_, err = conn.Write(connectRequest)
require.NoError(t, err)
_, err = readSOCKS5ResponseAddress(t, conn)
require.NoError(t, err)
return conn
}
func dialSOCKS5UDPAssociate(t *testing.T, proxyAddr, username, password string) (net.Conn, *net.UDPConn) {
t.Helper()
controlConn, err := (&net.Dialer{}).DialContext(t.Context(), "tcp", proxyAddr)
require.NoError(t, err)
negotiateSOCKS5(t, controlConn, username, password)
udpAssociateRequest := []byte{socks5Version, byte(udpAssociate), 0, byte(ipv4), 0, 0, 0, 0, 0, 0}
_, err = controlConn.Write(udpAssociateRequest)
require.NoError(t, err)
udpProxyAddress, err := readSOCKS5ResponseAddress(t, controlConn)
require.NoError(t, err)
udpProxyResolvedAddress, err := net.ResolveUDPAddr("udp", udpProxyAddress)
require.NoError(t, err)
udpConn, err := net.DialUDP("udp", nil, udpProxyResolvedAddress)
require.NoError(t, err)
return controlConn, udpConn
}
func negotiateSOCKS5(t *testing.T, conn net.Conn, username, password string) {
t.Helper()
var err error
var method authMethod var method authMethod
if username != "" || password != "" { if username != "" || password != "" {
method = authUsernamePassword method = authUsernamePassword
@@ -138,45 +363,146 @@ func dialSOCKS5(t *testing.T, proxyAddr, targetAddr, username, password string)
require.Equal(t, authUsernamePasswordSubNegotiation1, subnegResp[0]) require.Equal(t, authUsernamePasswordSubNegotiation1, subnegResp[0])
require.Equal(t, byte(0), subnegResp[1]) require.Equal(t, byte(0), subnegResp[1])
} }
}
var connectRequest []byte func readSOCKS5ResponseAddress(t *testing.T, conn net.Conn) (address string, err error) {
if ip := net.ParseIP(host).To4(); ip != nil { t.Helper()
connectRequest = []byte{socks5Version, byte(connect), 0, byte(ipv4)}
connectRequest = append(connectRequest, ip...)
} else {
connectRequest = []byte{socks5Version, byte(connect), 0, byte(domainName), byte(len(host))}
connectRequest = append(connectRequest, []byte(host)...)
}
connectRequest = binary.BigEndian.AppendUint16(connectRequest, uint16(targetPort)) //nolint:gosec
_, err = conn.Write(connectRequest)
require.NoError(t, err)
var responseHeader [4]byte var responseHeader [4]byte
_, err = io.ReadFull(conn, responseHeader[:]) _, err = io.ReadFull(conn, responseHeader[:])
require.NoError(t, err) if err != nil {
require.Equal(t, socks5Version, responseHeader[0]) return "", err
require.Equal(t, byte(succeeded), responseHeader[1]) }
if responseHeader[0] != socks5Version {
// Consume BND.ADDR and BND.PORT (their values are irrelevant to the caller). return "", errors.New("version mismatch")
switch addrType(responseHeader[3]) { }
case ipv4: if responseHeader[1] != byte(succeeded) {
var addrPort [net.IPv4len + 2]byte return "", errors.New("request was not successful")
_, err = io.ReadFull(conn, addrPort[:])
require.NoError(t, err)
case ipv6:
var addrPort [net.IPv6len + 2]byte
_, err = io.ReadFull(conn, addrPort[:])
require.NoError(t, err)
case domainName:
var lenBuf [1]byte
_, err = io.ReadFull(conn, lenBuf[:])
require.NoError(t, err)
addrPort := make([]byte, int(lenBuf[0])+2)
_, err = io.ReadFull(conn, addrPort)
require.NoError(t, err)
} }
return conn var host string
switch addrType(responseHeader[3]) {
case ipv4:
addressAndPort := make([]byte, net.IPv4len+2)
_, err = io.ReadFull(conn, addressAndPort)
if err != nil {
return "", err
}
host = net.IP(addressAndPort[:net.IPv4len]).String()
port := binary.BigEndian.Uint16(addressAndPort[net.IPv4len:])
return net.JoinHostPort(host, strconv.FormatUint(uint64(port), 10)), nil
case ipv6:
addressAndPort := make([]byte, net.IPv6len+2)
_, err = io.ReadFull(conn, addressAndPort)
if err != nil {
return "", err
}
host = net.IP(addressAndPort[:net.IPv6len]).String()
port := binary.BigEndian.Uint16(addressAndPort[net.IPv6len:])
return net.JoinHostPort(host, strconv.FormatUint(uint64(port), 10)), nil
case domainName:
var lengthBuffer [1]byte
_, err = io.ReadFull(conn, lengthBuffer[:])
if err != nil {
return "", err
}
domainAndPort := make([]byte, int(lengthBuffer[0])+2)
_, err = io.ReadFull(conn, domainAndPort)
if err != nil {
return "", err
}
host = string(domainAndPort[:len(domainAndPort)-2])
port := binary.BigEndian.Uint16(domainAndPort[len(domainAndPort)-2:])
return net.JoinHostPort(host, strconv.FormatUint(uint64(port), 10)), nil
default:
return "", errors.New("unknown address type")
}
}
func makeSOCKS5UDPDatagram(targetAddress string, payload []byte) ([]byte, error) {
host, portString, err := net.SplitHostPort(targetAddress)
if err != nil {
return nil, err
}
port, err := strconv.ParseUint(portString, 10, 16)
if err != nil {
return nil, err
}
datagram := []byte{0, 0, 0}
ipAddress := net.ParseIP(host)
if ipAddress != nil {
if ipAddress.To4() != nil {
datagram = append(datagram, byte(ipv4))
datagram = append(datagram, ipAddress.To4()...)
} else {
datagram = append(datagram, byte(ipv6))
datagram = append(datagram, ipAddress.To16()...)
}
} else {
if len(host) > 255 {
return nil, errors.New("domain name too long")
}
datagram = append(datagram, byte(domainName), byte(len(host)))
datagram = append(datagram, []byte(host)...)
}
datagram = binary.BigEndian.AppendUint16(datagram, uint16(port))
datagram = append(datagram, payload...)
return datagram, nil
}
func parseSOCKS5UDPDatagram(datagram []byte) (destinationAddress string, payload []byte, err error) {
if len(datagram) < 4 {
return "", nil, errors.New("datagram too short")
}
if datagram[0] != 0 || datagram[1] != 0 {
return "", nil, errors.New("invalid reserved header")
}
if datagram[2] != 0 {
return "", nil, errors.New("fragments are not supported")
}
offset := 3
var host string
switch addrType(datagram[offset]) {
case ipv4:
offset++
if len(datagram) < offset+net.IPv4len+2 {
return "", nil, errors.New("datagram too short for IPv4")
}
host = net.IP(datagram[offset : offset+net.IPv4len]).String()
offset += net.IPv4len
case ipv6:
offset++
if len(datagram) < offset+net.IPv6len+2 {
return "", nil, errors.New("datagram too short for IPv6")
}
host = net.IP(datagram[offset : offset+net.IPv6len]).String()
offset += net.IPv6len
case domainName:
offset++
if len(datagram) < offset+1 {
return "", nil, errors.New("datagram too short for domain length")
}
domainLength := int(datagram[offset])
offset++
if len(datagram) < offset+domainLength+2 {
return "", nil, errors.New("datagram too short for domain")
}
host = string(datagram[offset : offset+domainLength])
offset += domainLength
default:
return "", nil, errors.New("unknown address type")
}
if len(datagram) < offset+2 {
return "", nil, errors.New("datagram too short for port")
}
port := binary.BigEndian.Uint16(datagram[offset : offset+2])
offset += 2
return net.JoinHostPort(host, strconv.FormatUint(uint64(port), 10)), datagram[offset:], nil
} }
func Test_newServer(t *testing.T) { func Test_newServer(t *testing.T) {
@@ -224,7 +550,8 @@ func Test_Server_StartStop(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
logger := NewMockLogger(ctrl) logger := NewMockLogger(ctrl)
logger.EXPECT().Infof("SOCKS5 server listening on %s", gomock.Any()) logger.EXPECT().Infof("SOCKS5 TCP server listening on %s", gomock.Any())
logger.EXPECT().Infof("SOCKS5 UDP server listening on %s", gomock.Any())
server := newServer(Settings{ server := newServer(Settings{
Address: "127.0.0.1:0", Address: "127.0.0.1:0",
@@ -377,6 +704,70 @@ func Test_decodeRequest(t *testing.T) {
} }
} }
func Test_udpAssociateExpectedClientEndpoint(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
request request
expected netip.AddrPort
expectedErr string
}{
"ipv4_endpoint": {
request: request{
addressType: ipv4,
destination: "192.0.2.10",
port: 5555,
},
expected: netip.MustParseAddrPort("192.0.2.10:5555"),
},
"ipv4_unspecified_address": {
request: request{
addressType: ipv4,
destination: "0.0.0.0",
port: 6000,
},
expected: netip.AddrPortFrom(netip.Addr{}, 6000),
},
"domain_name_with_port": {
request: request{
addressType: domainName,
destination: "client.example",
port: 7000,
},
expected: netip.AddrPortFrom(netip.Addr{}, 7000),
},
"domain_name_without_port": {
request: request{
addressType: domainName,
destination: "client.example",
},
expected: netip.AddrPort{},
},
"unsupported_address_type": {
request: request{
addressType: 255,
},
expectedErr: "address type 255 is not supported",
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
result, err := udpAssociateExpectedClientEndpoint(testCase.request)
if testCase.expectedErr != "" {
assert.ErrorContains(t, err, testCase.expectedErr)
return
}
assert.NoError(t, err)
assert.Equal(t, testCase.expected, result)
})
}
}
func Test_verifyFirstNegotiation(t *testing.T) { func Test_verifyFirstNegotiation(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
@@ -598,10 +989,6 @@ func Test_cmdType_String(t *testing.T) {
cmd: connect, cmd: connect,
expectedName: "connect", expectedName: "connect",
}, },
"bind": {
cmd: bind,
expectedName: "bind",
},
"udp_associate": { "udp_associate": {
cmd: udpAssociate, cmd: udpAssociate,
expectedName: "UDP associate", expectedName: "UDP associate",
@@ -620,3 +1007,80 @@ func Test_cmdType_String(t *testing.T) {
}) })
} }
} }
func Test_socksConn_udpAssociationAddresses(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
routerAddress string
expectAddressFromConn bool
expectedAddress string
}{
"wildcard_router_address_uses_control_connection_local_ip": {
routerAddress: ":0",
expectAddressFromConn: true,
},
"concrete_router_address_is_kept": {
routerAddress: "127.0.0.1:0",
expectedAddress: "127.0.0.1",
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()
router, err := newUDPRouter(t.Context(), testCase.routerAddress, noopLogger{})
require.NoError(t, err)
t.Cleanup(func() {
err := router.close()
assert.NoError(t, err)
})
controlListener, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", "127.0.0.1:0")
require.NoError(t, err)
t.Cleanup(func() {
err := controlListener.Close()
assert.NoError(t, err)
})
acceptedConnCh := make(chan net.Conn, 1)
go func() {
acceptedConn, acceptErr := controlListener.Accept()
if acceptErr != nil {
return
}
acceptedConnCh <- acceptedConn
}()
clientControlConn, err := (&net.Dialer{}).DialContext(t.Context(), "tcp", controlListener.Addr().String())
require.NoError(t, err)
defer clientControlConn.Close()
serverControlConn := <-acceptedConnCh
defer serverControlConn.Close()
socksConnection := &socksConn{
clientConn: clientControlConn,
udpRouter: router,
}
bindAddress, bindPort, bindAddrType, err := socksConnection.udpAssociationAddresses()
require.NoError(t, err)
if testCase.expectAddressFromConn {
clientLocalHost, _, err := net.SplitHostPort(clientControlConn.LocalAddr().String())
require.NoError(t, err)
assert.Equal(t, clientLocalHost, bindAddress)
} else {
assert.Equal(t, testCase.expectedAddress, bindAddress)
}
_, routerPortString, err := net.SplitHostPort(router.localAddress().String())
require.NoError(t, err)
routerPort, err := strconv.ParseUint(routerPortString, 10, 16)
require.NoError(t, err)
assert.Equal(t, uint16(routerPort), bindPort)
assert.Equal(t, ipv4, bindAddrType)
})
}
}
+370
View File
@@ -0,0 +1,370 @@
package socks5
import (
"bytes"
"context"
"errors"
"fmt"
"net"
"net/netip"
"sync"
)
type udpAssociation struct {
id uint64
clientAddrPort netip.AddrPort
expectedAddrPort netip.AddrPort
controlConnAddr netip.Addr
packetCh chan *bytes.Buffer
}
type udpRouter struct {
logger Logger
listener net.PacketConn
mutex sync.Mutex
bufferPool sync.Pool
nextAssociationID uint64
clientAddrPortToAssociation map[netip.AddrPort]udpAssociation
clientIPToPendingAssociations map[netip.Addr][]udpAssociation
associationIDToClientAddrPort map[uint64]netip.AddrPort
}
const (
maxUDPPacketLength = 65535
maxSOCKS5UDPDatagramOverhead = 3 + 1 + 16 + 2
pooledUDPPacketBufferCapacity = maxUDPPacketLength + maxSOCKS5UDPDatagramOverhead
)
func newUDPRouter(ctx context.Context, address string, logger Logger) (router *udpRouter, err error) {
config := &net.ListenConfig{}
listener, err := config.ListenPacket(ctx, "udp", address)
if err != nil {
return nil, fmt.Errorf("UDP listening: %w", err)
}
return &udpRouter{
logger: logger,
listener: listener,
bufferPool: sync.Pool{
New: func() any {
return bytes.NewBuffer(make([]byte, 0, pooledUDPPacketBufferCapacity))
},
},
nextAssociationID: 1,
clientAddrPortToAssociation: make(map[netip.AddrPort]udpAssociation),
clientIPToPendingAssociations: make(map[netip.Addr][]udpAssociation),
associationIDToClientAddrPort: make(map[uint64]netip.AddrPort),
}, nil
}
func (r *udpRouter) localAddress() net.Addr {
return r.listener.LocalAddr()
}
func (r *udpRouter) close() error {
return r.listener.Close()
}
func (r *udpRouter) registerAssociation(controlConn net.Conn, expectedAddrPort netip.AddrPort) (udpAssociation, error) {
controlConnAddrPort, err := netip.ParseAddrPort(controlConn.RemoteAddr().String())
if err != nil {
return udpAssociation{}, fmt.Errorf("parsing control connection address: %w", err)
}
controlConnAddr := controlConnAddrPort.Addr().Unmap()
r.mutex.Lock()
defer r.mutex.Unlock()
const udpPacketChannelBuffer = 64
associationID := r.nextAssociationID
r.nextAssociationID++
association := udpAssociation{
id: associationID,
expectedAddrPort: expectedAddrPort,
controlConnAddr: controlConnAddr,
packetCh: make(chan *bytes.Buffer, udpPacketChannelBuffer),
}
if expectedAddrPort.Addr().IsValid() && expectedAddrPort.Port() != 0 {
association.clientAddrPort = expectedAddrPort
r.clientAddrPortToAssociation[association.clientAddrPort] = association
r.associationIDToClientAddrPort[association.id] = association.clientAddrPort
return association, nil
}
pendingAssociations := r.clientIPToPendingAssociations[controlConnAddr]
pendingAssociations = append(pendingAssociations, association)
r.clientIPToPendingAssociations[controlConnAddr] = pendingAssociations
return association, nil
}
func (r *udpRouter) unregisterAssociation(association udpAssociation) {
r.mutex.Lock()
defer r.mutex.Unlock()
clientAddrPort, hasClientAddress := r.associationIDToClientAddrPort[association.id]
if hasClientAddress {
delete(r.associationIDToClientAddrPort, association.id)
delete(r.clientAddrPortToAssociation, clientAddrPort)
}
pendingAssociations := r.clientIPToPendingAssociations[association.controlConnAddr]
for i, pendingAssociation := range pendingAssociations {
if pendingAssociation.id == association.id {
pendingAssociations = append(pendingAssociations[:i], pendingAssociations[i+1:]...)
break
}
}
if len(pendingAssociations) == 0 {
delete(r.clientIPToPendingAssociations, association.controlConnAddr)
} else {
r.clientIPToPendingAssociations[association.controlConnAddr] = pendingAssociations
}
}
func (r *udpRouter) run(ctx context.Context) error {
packetBuffer := make([]byte, maxUDPPacketLength)
for {
packetLength, sourceAddress, err := r.listener.ReadFrom(packetBuffer)
if err != nil {
if ctx.Err() != nil && errors.Is(err, net.ErrClosed) {
return nil
}
return fmt.Errorf("reading UDP packet: %w", err)
}
sourceAddrPort, err := netAddrToNetipAddrPort(sourceAddress)
if err != nil {
r.logger.Warnf("parsing source address: %s", err)
continue
}
buffer := r.bufferPool.Get().(*bytes.Buffer) //nolint:forcetypeassert
buffer.Reset()
_, err = buffer.Write(packetBuffer[:packetLength])
if err != nil {
r.bufferPool.Put(buffer)
r.logger.Warnf("buffering packet: %s", err)
continue
}
err = r.routePacket(sourceAddrPort, buffer)
if err != nil {
r.logger.Warnf("failed routing UDP packet: %s", err)
}
}
}
func (r *udpRouter) routePacket(sourceAddrPort netip.AddrPort, packet *bytes.Buffer) error {
r.mutex.Lock()
association, packetFromClient := r.findClientAssociation(sourceAddrPort)
r.mutex.Unlock()
if !packetFromClient {
r.bufferPool.Put(packet)
return nil
}
select {
case association.packetCh <- packet:
return nil
default:
r.bufferPool.Put(packet)
return errors.New("association packet queue full")
}
}
func (r *udpRouter) findClientAssociation(sourceAddrPort netip.AddrPort) (
association udpAssociation, ok bool,
) {
association, ok = r.clientAddrPortToAssociation[sourceAddrPort]
if ok {
return association, true
}
sourceAddr := sourceAddrPort.Addr()
pendingAssociations := r.clientIPToPendingAssociations[sourceAddr]
if len(pendingAssociations) == 0 {
return udpAssociation{}, false
}
index := -1
for i, pendingAssociation := range pendingAssociations {
if matchesExpectedClientEndpoint(pendingAssociation, sourceAddrPort) {
association = pendingAssociation
index = i
break
}
}
if index == -1 {
return udpAssociation{}, false
}
r.clientIPToPendingAssociations[sourceAddr] = append(pendingAssociations[:index], pendingAssociations[index+1:]...)
if len(r.clientIPToPendingAssociations[sourceAddr]) == 0 {
delete(r.clientIPToPendingAssociations, sourceAddr)
}
association.clientAddrPort = sourceAddrPort
r.clientAddrPortToAssociation[sourceAddrPort] = association
r.associationIDToClientAddrPort[association.id] = sourceAddrPort
return association, true
}
func matchesExpectedClientEndpoint(association udpAssociation, sourceAddrPort netip.AddrPort) bool {
switch {
case association.expectedAddrPort.Addr().IsValid() && sourceAddrPort.Addr() != association.expectedAddrPort.Addr():
return false
case association.expectedAddrPort.Port() != 0 && sourceAddrPort.Port() != association.expectedAddrPort.Port():
return false
}
return true
}
func (r *udpRouter) clientAddrPortForAssociation(associationID uint64) (
clientAddrPort netip.AddrPort, ok bool,
) {
r.mutex.Lock()
defer r.mutex.Unlock()
clientAddrPort, ok = r.associationIDToClientAddrPort[associationID]
return clientAddrPort, ok
}
func (r *udpRouter) runAssociationHandler(ctx context.Context, association udpAssociation) {
config := &net.ListenConfig{}
socket, err := config.ListenPacket(ctx, "udp", ":0")
if err != nil {
r.logger.Warnf("creating per-association UDP socket: %s", err)
return
}
defer socket.Close()
go closeSocketOnContextDone(ctx, socket)
packetBuffer := make([]byte, maxUDPPacketLength)
forwardDoneCh := make(chan struct{})
go r.forwardClientPackets(ctx, socket, association.packetCh, forwardDoneCh)
for {
packetLength, sourceAddress, err := socket.ReadFrom(packetBuffer)
if err != nil {
if ctx.Err() != nil || errors.Is(err, net.ErrClosed) {
<-forwardDoneCh
return
}
r.logger.Warnf("reading from per-association UDP socket: %s", err)
continue
}
sourceAddrPort, err := netAddrToNetipAddrPort(sourceAddress)
if err != nil {
r.logger.Warnf("parsing source address from destination: %s", err)
continue
}
buffer := r.bufferPool.Get().(*bytes.Buffer) //nolint:forcetypeassert
buffer.Reset()
err = encodeUDPDatagramToBuffer(buffer, sourceAddrPort, packetBuffer[:packetLength])
if err != nil {
r.bufferPool.Put(buffer)
r.logger.Warnf("encoding response datagram: %s", err)
continue
}
clientAddrPort, found := r.clientAddrPortForAssociation(association.id)
if !found {
r.bufferPool.Put(buffer)
r.logger.Warnf("client address not found for association id %d", association.id)
continue
}
clientUDPAddress := &net.UDPAddr{
IP: clientAddrPort.Addr().AsSlice(),
Port: int(clientAddrPort.Port()),
}
_, err = r.listener.WriteTo(buffer.Bytes(), clientUDPAddress)
r.bufferPool.Put(buffer)
if err != nil {
r.logger.Warnf("writing response to client: %s", err)
}
}
}
func closeSocketOnContextDone(ctx context.Context, socket net.PacketConn) {
<-ctx.Done()
_ = socket.Close()
}
func (r *udpRouter) forwardClientPackets(ctx context.Context, socket net.PacketConn,
packetCh <-chan *bytes.Buffer, done chan<- struct{},
) {
defer close(done)
for {
select {
case <-ctx.Done():
return
case buffer, ok := <-packetCh:
if !ok {
return
}
err := r.writeClientPacketToDestination(ctx, socket, buffer)
r.bufferPool.Put(buffer)
if err != nil {
r.logger.Warnf("forwarding client packet to destination: %s", err)
}
}
}
}
func (r *udpRouter) writeClientPacketToDestination(ctx context.Context,
socket net.PacketConn, packet *bytes.Buffer,
) error {
destination, payload, err := decodeUDPDatagram(packet.Bytes())
if err != nil {
return fmt.Errorf("decoding UDP datagram: %w", err)
}
host, portStr, err := net.SplitHostPort(destination)
if err != nil {
return fmt.Errorf("splitting destination host and port: %w", err)
}
if _, err := netip.ParseAddr(host); err != nil { // domain name
addrs, err := net.DefaultResolver.LookupHost(ctx, host)
if err != nil {
return fmt.Errorf("resolving destination host: %w", err)
}
if len(addrs) == 0 {
return fmt.Errorf("resolving destination host: no addresses found for %q", host)
}
destination = net.JoinHostPort(addrs[0], portStr)
}
resolvedDestinationUDPAddress, err := net.ResolveUDPAddr("udp", destination)
if err != nil {
return fmt.Errorf("resolving destination UDP address: %w", err)
}
_, err = socket.WriteTo(payload, resolvedDestinationUDPAddress)
if err != nil && ctx.Err() == nil {
return fmt.Errorf("writing payload to destination: %w", err)
}
return nil
}
func netAddrToNetipAddrPort(addr net.Addr) (netip.AddrPort, error) {
addrPort, err := netip.ParseAddrPort(addr.String())
if err != nil {
return netip.AddrPort{}, fmt.Errorf("parsing address: %w", err)
}
return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil
}
@@ -0,0 +1,164 @@
//go:build integration
package socks5
import (
"bytes"
"context"
"math/rand/v2"
"net"
"net/netip"
"strconv"
"testing"
"time"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func Test_udpRouter_ResolveGithubFromCloudflareDNS(t *testing.T) {
t.Parallel()
ctx := t.Context()
var cancel context.CancelFunc
deadline, hasDeadline := ctx.Deadline()
if hasDeadline {
const deadlineBuffer = 500 * time.Millisecond
deadline = deadline.Add(-deadlineBuffer)
} else {
const defaultTimeout = 10 * time.Second
deadline = time.Now().Add(defaultTimeout)
}
ctx, cancel = context.WithDeadline(ctx, deadline)
ctrl := gomock.NewController(t)
logger := NewMockLogger(ctrl)
router, err := newUDPRouter(ctx, "127.0.0.1:0", logger)
require.NoError(t, err)
routerRunErrCh := make(chan error)
go func() {
routerRunErrCh <- router.run(ctx)
}()
t.Cleanup(func() {
cancel()
err := router.close()
assert.NoError(t, err, "closing router")
runErr := <-routerRunErrCh
assert.NoError(t, runErr)
})
controlListener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:0")
require.NoError(t, err)
t.Cleanup(func() {
err := controlListener.Close()
assert.NoError(t, err, "closing control listener")
})
acceptedConnCh := make(chan net.Conn)
go func() {
acceptedConn, acceptErr := controlListener.Accept()
assert.NoError(t, acceptErr, "accepting control connection")
if acceptErr != nil {
return
}
acceptedConnCh <- acceptedConn
}()
clientControlConn, err := (&net.Dialer{}).DialContext(ctx, "tcp", controlListener.Addr().String())
require.NoError(t, err)
t.Cleanup(func() {
err = clientControlConn.Close()
assert.NoError(t, err, "closing client control connection")
})
serverControlConn := <-acceptedConnCh
t.Cleanup(func() {
err := serverControlConn.Close()
assert.NoError(t, err, "closing server control connection")
})
association, err := router.registerAssociation(serverControlConn, netip.AddrPort{})
require.NoError(t, err)
t.Cleanup(func() {
router.unregisterAssociation(association)
})
associationCtx, associationCancel := context.WithCancel(ctx)
handlerDoneCh := make(chan struct{})
go func() {
router.runAssociationHandler(associationCtx, association)
close(handlerDoneCh)
}()
t.Cleanup(func() {
associationCancel()
<-handlerDoneCh
})
udpRouterAddress, err := net.ResolveUDPAddr("udp", router.localAddress().String())
require.NoError(t, err)
clientUDPConn, err := net.DialUDP("udp", nil, udpRouterAddress)
require.NoError(t, err)
t.Cleanup(func() {
err := clientUDPConn.Close()
assert.NoError(t, err, "closing client UDP connection")
})
queryID := uint16(rand.Uint32()) //nolint:gosec
dnsRequest := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: queryID,
RecursionDesired: true,
},
Question: []dns.Question{{
Name: dns.Fqdn("github.com"),
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}},
}
dnsQuery, err := dnsRequest.Pack()
require.NoError(t, err)
targetAddrPort := netip.MustParseAddrPort("1.1.1.1:53")
socksDatagramBuffer := bytes.NewBuffer(nil)
err = encodeUDPDatagramToBuffer(socksDatagramBuffer, targetAddrPort, dnsQuery)
require.NoError(t, err)
socksDatagram := socksDatagramBuffer.Bytes()
err = clientUDPConn.SetDeadline(deadline)
require.NoError(t, err)
_, err = clientUDPConn.Write(socksDatagram)
require.NoError(t, err)
responseBuffer := make([]byte, maxUDPPacketLength)
responseLength, err := clientUDPConn.Read(responseBuffer)
require.NoError(t, err)
responseDestination, responsePayload, err := decodeUDPDatagram(responseBuffer[:responseLength])
require.NoError(t, err)
responseHost, responsePortString, err := net.SplitHostPort(responseDestination)
require.NoError(t, err)
responsePort, err := strconv.ParseUint(responsePortString, 10, 16)
require.NoError(t, err)
assert.Equal(t, uint64(53), responsePort)
assert.NotEmpty(t, responseHost)
dnsResponse := new(dns.Msg)
err = dnsResponse.Unpack(responsePayload)
require.NoError(t, err)
assert.Equal(t, queryID, dnsResponse.Id)
assert.True(t, dnsResponse.Response)
assert.Equal(t, dns.RcodeSuccess, dnsResponse.Rcode)
require.NotEmpty(t, dnsResponse.Question)
assert.Equal(t, dns.Fqdn("github.com"), dnsResponse.Question[0].Name)
assert.Equal(t, dns.TypeA, dnsResponse.Question[0].Qtype)
assert.NotEmpty(t, dnsResponse.Answer)
require.NoError(t, err)
}
+20 -14
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/storage (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/storage (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=mocks_test.go -package storage . Logger
//
// Package storage is a generated GoMock package. // Package storage is a generated GoMock package.
package storage package storage
@@ -7,13 +12,14 @@ package storage
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,42 +40,42 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", s)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), s)
} }
// Infof mocks base method. // Infof mocks base method.
func (m *MockLogger) Infof(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Infof(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Infof", varargs...) m.ctrl.Call(m, "Infof", varargs...)
} }
// Infof indicates an expected call of Infof. // Infof indicates an expected call of Infof.
func (mr *MockLoggerMockRecorder) Infof(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Infof(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLogger)(nil).Infof), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLogger)(nil).Infof), varargs...)
} }
// Warn mocks base method. // Warn mocks base method.
func (m *MockLogger) Warn(arg0 string) { func (m *MockLogger) Warn(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0) m.ctrl.Call(m, "Warn", s)
} }
// Warn indicates an expected call of Warn. // Warn indicates an expected call of Warn.
func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Warn(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), s)
} }
+1 -1
View File
@@ -4,11 +4,11 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func populateProviderToVersion(providerToVersion map[string]uint16) map[string]uint16 { func populateProviderToVersion(providerToVersion map[string]uint16) map[string]uint16 {
+1 -1
View File
@@ -5,9 +5,9 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_AddAddresses(t *testing.T) { func Test_AddAddresses(t *testing.T) {
+29 -23
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/wireguard (interfaces: Logger) // Source: github.com/qdm12/gluetun/internal/wireguard (interfaces: Logger)
//
// Generated by this command:
//
// mockgen -destination=log_mock_test.go -package wireguard . Logger
//
// Package wireguard is a generated GoMock package. // Package wireguard is a generated GoMock package.
package wireguard package wireguard
@@ -7,13 +12,14 @@ package wireguard
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockLogger is a mock of Logger interface. // MockLogger is a mock of Logger interface.
type MockLogger struct { type MockLogger struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockLoggerMockRecorder recorder *MockLoggerMockRecorder
isgomock struct{}
} }
// MockLoggerMockRecorder is the mock recorder for MockLogger. // MockLoggerMockRecorder is the mock recorder for MockLogger.
@@ -34,71 +40,71 @@ func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
} }
// Debug mocks base method. // Debug mocks base method.
func (m *MockLogger) Debug(arg0 string) { func (m *MockLogger) Debug(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Debug", arg0) m.ctrl.Call(m, "Debug", s)
} }
// Debug indicates an expected call of Debug. // Debug indicates an expected call of Debug.
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debug(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), s)
} }
// Debugf mocks base method. // Debugf mocks base method.
func (m *MockLogger) Debugf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Debugf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Debugf", varargs...) m.ctrl.Call(m, "Debugf", varargs...)
} }
// Debugf indicates an expected call of Debugf. // Debugf indicates an expected call of Debugf.
func (mr *MockLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Debugf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...)
} }
// Error mocks base method. // Error mocks base method.
func (m *MockLogger) Error(arg0 string) { func (m *MockLogger) Error(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Error", arg0) m.ctrl.Call(m, "Error", s)
} }
// Error indicates an expected call of Error. // Error indicates an expected call of Error.
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Error(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), s)
} }
// Errorf mocks base method. // Errorf mocks base method.
func (m *MockLogger) Errorf(arg0 string, arg1 ...interface{}) { func (m *MockLogger) Errorf(format string, args ...any) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{arg0} varargs := []any{format}
for _, a := range arg1 { for _, a := range args {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
m.ctrl.Call(m, "Errorf", varargs...) m.ctrl.Call(m, "Errorf", varargs...)
} }
// Errorf indicates an expected call of Errorf. // Errorf indicates an expected call of Errorf.
func (mr *MockLoggerMockRecorder) Errorf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Errorf(format any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0}, arg1...) varargs := append([]any{format}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...)
} }
// Info mocks base method. // Info mocks base method.
func (m *MockLogger) Info(arg0 string) { func (m *MockLogger) Info(s string) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "Info", arg0) m.ctrl.Call(m, "Info", s)
} }
// Info indicates an expected call of Info. // Info indicates an expected call of Info.
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { func (mr *MockLoggerMockRecorder) Info(s any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), s)
} }
+1 -1
View File
@@ -3,7 +3,7 @@ package wireguard
import ( import (
"testing" "testing"
"github.com/golang/mock/gomock" "go.uber.org/mock/gomock"
) )
func Test_makeDeviceLogger(t *testing.T) { func Test_makeDeviceLogger(t *testing.T) {
+47 -41
View File
@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/wireguard (interfaces: NetLinker) // Source: github.com/qdm12/gluetun/internal/wireguard (interfaces: NetLinker)
//
// Generated by this command:
//
// mockgen -destination=netlinker_mock_test.go -package wireguard . NetLinker
//
// Package wireguard is a generated GoMock package. // Package wireguard is a generated GoMock package.
package wireguard package wireguard
@@ -8,14 +13,15 @@ import (
netip "net/netip" netip "net/netip"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
netlink "github.com/qdm12/gluetun/internal/netlink" netlink "github.com/qdm12/gluetun/internal/netlink"
gomock "go.uber.org/mock/gomock"
) )
// MockNetLinker is a mock of NetLinker interface. // MockNetLinker is a mock of NetLinker interface.
type MockNetLinker struct { type MockNetLinker struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockNetLinkerMockRecorder recorder *MockNetLinkerMockRecorder
isgomock struct{}
} }
// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker. // MockNetLinkerMockRecorder is the mock recorder for MockNetLinker.
@@ -36,17 +42,17 @@ func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder {
} }
// AddrReplace mocks base method. // AddrReplace mocks base method.
func (m *MockNetLinker) AddrReplace(arg0 uint32, arg1 netip.Prefix) error { func (m *MockNetLinker) AddrReplace(linkIndex uint32, addr netip.Prefix) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrReplace", arg0, arg1) ret := m.ctrl.Call(m, "AddrReplace", linkIndex, addr)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// AddrReplace indicates an expected call of AddrReplace. // AddrReplace indicates an expected call of AddrReplace.
func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) AddrReplace(linkIndex, addr any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), linkIndex, addr)
} }
// IsWireguardSupported mocks base method. // IsWireguardSupported mocks base method.
@@ -65,47 +71,47 @@ func (mr *MockNetLinkerMockRecorder) IsWireguardSupported() *gomock.Call {
} }
// LinkAdd mocks base method. // LinkAdd mocks base method.
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) (uint32, error) { func (m *MockNetLinker) LinkAdd(link netlink.Link) (uint32, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkAdd", arg0) ret := m.ctrl.Call(m, "LinkAdd", link)
ret0, _ := ret[0].(uint32) ret0, _ := ret[0].(uint32)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkAdd indicates an expected call of LinkAdd. // LinkAdd indicates an expected call of LinkAdd.
func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkAdd(link any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), link)
} }
// LinkByName mocks base method. // LinkByName mocks base method.
func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) { func (m *MockNetLinker) LinkByName(name string) (netlink.Link, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByName", arg0) ret := m.ctrl.Call(m, "LinkByName", name)
ret0, _ := ret[0].(netlink.Link) ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LinkByName indicates an expected call of LinkByName. // LinkByName indicates an expected call of LinkByName.
func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkByName(name any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), name)
} }
// LinkDel mocks base method. // LinkDel mocks base method.
func (m *MockNetLinker) LinkDel(arg0 uint32) error { func (m *MockNetLinker) LinkDel(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkDel", arg0) ret := m.ctrl.Call(m, "LinkDel", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkDel indicates an expected call of LinkDel. // LinkDel indicates an expected call of LinkDel.
func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkDel(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), linkIndex)
} }
// LinkList mocks base method. // LinkList mocks base method.
@@ -124,86 +130,86 @@ func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call {
} }
// LinkSetDown mocks base method. // LinkSetDown mocks base method.
func (m *MockNetLinker) LinkSetDown(arg0 uint32) error { func (m *MockNetLinker) LinkSetDown(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetDown", arg0) ret := m.ctrl.Call(m, "LinkSetDown", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetDown indicates an expected call of LinkSetDown. // LinkSetDown indicates an expected call of LinkSetDown.
func (mr *MockNetLinkerMockRecorder) LinkSetDown(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetDown(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), linkIndex)
} }
// LinkSetUp mocks base method. // LinkSetUp mocks base method.
func (m *MockNetLinker) LinkSetUp(arg0 uint32) error { func (m *MockNetLinker) LinkSetUp(linkIndex uint32) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetUp", arg0) ret := m.ctrl.Call(m, "LinkSetUp", linkIndex)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// LinkSetUp indicates an expected call of LinkSetUp. // LinkSetUp indicates an expected call of LinkSetUp.
func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) LinkSetUp(linkIndex any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), linkIndex)
} }
// RouteAdd mocks base method. // RouteAdd mocks base method.
func (m *MockNetLinker) RouteAdd(arg0 netlink.Route) error { func (m *MockNetLinker) RouteAdd(route netlink.Route) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteAdd", arg0) ret := m.ctrl.Call(m, "RouteAdd", route)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RouteAdd indicates an expected call of RouteAdd. // RouteAdd indicates an expected call of RouteAdd.
func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteAdd(route any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), route)
} }
// RouteList mocks base method. // RouteList mocks base method.
func (m *MockNetLinker) RouteList(arg0 byte) ([]netlink.Route, error) { func (m *MockNetLinker) RouteList(family uint8) ([]netlink.Route, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteList", arg0) ret := m.ctrl.Call(m, "RouteList", family)
ret0, _ := ret[0].([]netlink.Route) ret0, _ := ret[0].([]netlink.Route)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// RouteList indicates an expected call of RouteList. // RouteList indicates an expected call of RouteList.
func (mr *MockNetLinkerMockRecorder) RouteList(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RouteList(family any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), family)
} }
// RuleAdd mocks base method. // RuleAdd mocks base method.
func (m *MockNetLinker) RuleAdd(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleAdd(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleAdd", arg0) ret := m.ctrl.Call(m, "RuleAdd", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleAdd indicates an expected call of RuleAdd. // RuleAdd indicates an expected call of RuleAdd.
func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleAdd(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), rule)
} }
// RuleDel mocks base method. // RuleDel mocks base method.
func (m *MockNetLinker) RuleDel(arg0 netlink.Rule) error { func (m *MockNetLinker) RuleDel(rule netlink.Rule) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleDel", arg0) ret := m.ctrl.Call(m, "RuleDel", rule)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// RuleDel indicates an expected call of RuleDel. // RuleDel indicates an expected call of RuleDel.
func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call { func (mr *MockNetLinkerMockRecorder) RuleDel(rule any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), rule)
} }
+1 -1
View File
@@ -5,10 +5,10 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/netlink" "github.com/qdm12/gluetun/internal/netlink"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_addRoute(t *testing.T) { func Test_addRoute(t *testing.T) {
+1 -1
View File
@@ -4,10 +4,10 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/netlink" "github.com/qdm12/gluetun/internal/netlink"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
) )
func Test_AddRule(t *testing.T) { func Test_AddRule(t *testing.T) {