From 2cf4d6b469c7510ebfe172f12901024eb7006dc4 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 17 Nov 2025 15:23:02 +0000 Subject: [PATCH 1/5] fix(protonvpn/updater): ignore casing when comparing received username --- internal/provider/protonvpn/updater/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/protonvpn/updater/api.go b/internal/provider/protonvpn/updater/api.go index 55ed516b..006f6739 100644 --- a/internal/provider/protonvpn/updater/api.go +++ b/internal/provider/protonvpn/updater/api.go @@ -371,7 +371,7 @@ func (c *apiClient) authInfo(ctx context.Context, username string, unauthCookie case info.SRPSession == "": return "", "", "", "", 0, fmt.Errorf("%w: SRP session is empty", ErrDataFieldMissing) - case info.Username != username: + case !strings.EqualFold(info.Username, username): return "", "", "", "", 0, fmt.Errorf("%w: expected %s got %s", ErrUsernameMismatch, username, info.Username) case info.Version == nil: From 6e99ca573e683cb3a3280b9fb75869385e172284 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 17 Nov 2025 15:29:41 +0000 Subject: [PATCH 2/5] chore(storage): do not read/write to user file when updating in maintainer mode --- internal/cli/update.go | 6 +++++- internal/storage/storage.go | 10 +++++++--- internal/storage/sync.go | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/cli/update.go b/internal/cli/update.go index ae05bd23..2acad8ae 100644 --- a/internal/cli/update.go +++ b/internal/cli/update.go @@ -81,7 +81,11 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e return fmt.Errorf("options validation failed: %w", err) } - storage, err := storage.New(logger, constants.ServersData) + serversDataPath := constants.ServersData + if maintainerMode { + serversDataPath = "" + } + storage, err := storage.New(logger, serversDataPath) if err != nil { return fmt.Errorf("creating servers storage: %w", err) } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 5210b07f..4dd7d3dd 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -23,7 +23,8 @@ type Infoer interface { // New creates a new storage and reads the servers from the // embedded servers file and the file on disk. -// Passing an empty filepath disables writing servers to a file. +// Passing an empty filepath disables the reading and writing of +// servers. func New(logger Infoer, filepath string) (storage *Storage, err error) { // A unit test prevents any error from being returned // and ensures all providers are part of the servers returned. @@ -31,12 +32,15 @@ func New(logger Infoer, filepath string) (storage *Storage, err error) { storage = &Storage{ hardcodedServers: hardcodedServers, + mergedServers: hardcodedServers, logger: logger, filepath: filepath, } - if err := storage.syncServers(); err != nil { - return nil, err + if filepath != "" { + if err := storage.syncServers(); err != nil { + return nil, err + } } return storage, nil diff --git a/internal/storage/sync.go b/internal/storage/sync.go index 1e0b539b..f31b5ca9 100644 --- a/internal/storage/sync.go +++ b/internal/storage/sync.go @@ -46,7 +46,7 @@ func (s *Storage) syncServers() (err error) { } // Eventually write file - if s.filepath == "" || reflect.DeepEqual(serversOnFile, s.mergedServers) { + if reflect.DeepEqual(serversOnFile, s.mergedServers) { return nil } From c4b9d459eda04d8edc9f8eb55d54ad8b74f3e4dd Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 17 Nov 2025 17:54:52 +0000 Subject: [PATCH 3/5] fix(dns): fix panic when using DNS_KEEP_NAMESERVER --- internal/dns/run.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/dns/run.go b/internal/dns/run.go index 2c67f9f4..430ab0ef 100644 --- a/internal/dns/run.go +++ b/internal/dns/run.go @@ -45,7 +45,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { if err == nil { l.backoffTime = defaultBackoffTime l.logger.Info("ready") - l.signalOrSetStatus(constants.Running) break } @@ -62,6 +61,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { l.logAndWait(ctx, err) settings = l.GetSettings() } + l.signalOrSetStatus(constants.Running) settings = l.GetSettings() if !*settings.KeepNameserver && !*settings.ServerEnabled { @@ -82,15 +82,19 @@ func (l *Loop) runWait(ctx context.Context, runError <-chan error) (exitLoop boo for { select { case <-ctx.Done(): - l.stopServer() - // TODO revert OS and Go nameserver when exiting + if !*l.GetSettings().KeepNameserver { + l.stopServer() + // TODO revert OS and Go nameserver when exiting + } return true case <-l.stop: l.userTrigger = true l.logger.Info("stopping") - const fallback = false - l.useUnencryptedDNS(fallback) - l.stopServer() + if !*l.GetSettings().KeepNameserver { + const fallback = false + l.useUnencryptedDNS(fallback) + l.stopServer() + } l.stopped <- struct{}{} case <-l.start: l.userTrigger = true From 9ed6cd978d94467a577e6dc6b09fae5adeaf8bf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:57:57 +0100 Subject: [PATCH 4/5] Chore(deps): Bump DavidAnson/markdownlint-cli2-action from 20 to 21 (#2984) --- .github/workflows/markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index dd8177ba..74782f4e 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v5 - - uses: DavidAnson/markdownlint-cli2-action@v20 + - uses: DavidAnson/markdownlint-cli2-action@v21 with: globs: "**.md" config: .markdownlint-cli2.jsonc From 41cd8fb30d16f3c5603a0359ab5d772036681c2e Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 17 Nov 2025 19:04:13 +0000 Subject: [PATCH 5/5] fix(storage): only log warning if flushing merged servers to file fails --- internal/cli/nooplogger.go | 1 + internal/storage/mocks_generate_test.go | 2 +- internal/storage/mocks_test.go | 42 ++++++++++++++++--------- internal/storage/read_test.go | 2 +- internal/storage/storage.go | 7 +++-- internal/storage/sync.go | 2 +- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/internal/cli/nooplogger.go b/internal/cli/nooplogger.go index 86753309..aa66ce50 100644 --- a/internal/cli/nooplogger.go +++ b/internal/cli/nooplogger.go @@ -7,3 +7,4 @@ func newNoopLogger() *noopLogger { } func (l *noopLogger) Info(string) {} +func (l *noopLogger) Warn(string) {} diff --git a/internal/storage/mocks_generate_test.go b/internal/storage/mocks_generate_test.go index 7227fef6..34399c07 100644 --- a/internal/storage/mocks_generate_test.go +++ b/internal/storage/mocks_generate_test.go @@ -1,3 +1,3 @@ package storage -//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Infoer +//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Logger diff --git a/internal/storage/mocks_test.go b/internal/storage/mocks_test.go index 7f701b14..e99aa5c3 100644 --- a/internal/storage/mocks_test.go +++ b/internal/storage/mocks_test.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/qdm12/gluetun/internal/storage (interfaces: Infoer) +// Source: github.com/qdm12/gluetun/internal/storage (interfaces: Logger) // Package storage is a generated GoMock package. package storage @@ -10,37 +10,49 @@ import ( gomock "github.com/golang/mock/gomock" ) -// MockInfoer is a mock of Infoer interface. -type MockInfoer struct { +// MockLogger is a mock of Logger interface. +type MockLogger struct { ctrl *gomock.Controller - recorder *MockInfoerMockRecorder + recorder *MockLoggerMockRecorder } -// MockInfoerMockRecorder is the mock recorder for MockInfoer. -type MockInfoerMockRecorder struct { - mock *MockInfoer +// MockLoggerMockRecorder is the mock recorder for MockLogger. +type MockLoggerMockRecorder struct { + mock *MockLogger } -// NewMockInfoer creates a new mock instance. -func NewMockInfoer(ctrl *gomock.Controller) *MockInfoer { - mock := &MockInfoer{ctrl: ctrl} - mock.recorder = &MockInfoerMockRecorder{mock} +// NewMockLogger creates a new mock instance. +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockInfoer) EXPECT() *MockInfoerMockRecorder { +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { return m.recorder } // Info mocks base method. -func (m *MockInfoer) Info(arg0 string) { +func (m *MockLogger) Info(arg0 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "Info", arg0) } // Info indicates an expected call of Info. -func (mr *MockInfoerMockRecorder) Info(arg0 interface{}) *gomock.Call { +func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockInfoer)(nil).Info), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) +} + +// Warn mocks base method. +func (m *MockLogger) Warn(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Warn", arg0) +} + +// Warn indicates an expected call of Warn. +func (mr *MockLoggerMockRecorder) Warn(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockLogger)(nil).Warn), arg0) } diff --git a/internal/storage/read_test.go b/internal/storage/read_test.go index 60e70efe..20bbe17a 100644 --- a/internal/storage/read_test.go +++ b/internal/storage/read_test.go @@ -95,7 +95,7 @@ func Test_extractServersFromBytes(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) - logger := NewMockInfoer(ctrl) + logger := NewMockLogger(ctrl) var previousLogCall *gomock.Call for _, logged := range testCase.logged { call := logger.EXPECT().Info(logged) diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 4dd7d3dd..ab0378f8 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -13,19 +13,20 @@ type Storage struct { // the embedded JSON file on every call to the // SyncServers method. hardcodedServers models.AllServers - logger Infoer + logger Logger filepath string } -type Infoer interface { +type Logger interface { Info(s string) + Warn(s string) } // New creates a new storage and reads the servers from the // embedded servers file and the file on disk. // Passing an empty filepath disables the reading and writing of // servers. -func New(logger Infoer, filepath string) (storage *Storage, err error) { +func New(logger Logger, filepath string) (storage *Storage, err error) { // A unit test prevents any error from being returned // and ensures all providers are part of the servers returned. hardcodedServers, _ := parseHardcodedServers() diff --git a/internal/storage/sync.go b/internal/storage/sync.go index f31b5ca9..de355c9c 100644 --- a/internal/storage/sync.go +++ b/internal/storage/sync.go @@ -52,7 +52,7 @@ func (s *Storage) syncServers() (err error) { err = s.flushToFile(s.filepath) if err != nil { - return fmt.Errorf("writing servers to file: %w", err) + s.logger.Warn("failed writing servers to file: " + err.Error()) } return nil }