From 7d3699345063da556b8fa658ff7fd5692c8b510b Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 8 Jul 2020 23:20:33 +0000 Subject: [PATCH] Tinyproxy run loop --- cmd/gluetun/main.go | 30 ++++------- internal/tinyproxy/loop.go | 106 +++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 internal/tinyproxy/loop.go diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 25370239..4576f24c 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -159,27 +159,6 @@ func _main(background context.Context, args []string) int { err = firewallConf.RunUserPostRules(ctx, fileManager, "/iptables/post-rules.txt") fatalOnError(err) - if allSettings.TinyProxy.Enabled { - err = tinyProxyConf.MakeConf( - allSettings.TinyProxy.LogLevel, - allSettings.TinyProxy.Port, - allSettings.TinyProxy.User, - allSettings.TinyProxy.Password, - uid, - gid) - fatalOnError(err) - err = firewallConf.AllowAnyIncomingOnPort(ctx, allSettings.TinyProxy.Port) - fatalOnError(err) - stream, waitFn, err := tinyProxyConf.Start(ctx) - fatalOnError(err) - waiter.Add(func() error { - err := waitFn() - logger.Error("tinyproxy: %s", err) - return err - }) - go streamMerger.Merge(ctx, stream, command.MergeName("tinyproxy"), command.MergeColor(constants.ColorTinyproxy())) - } - if allSettings.ShadowSocks.Enabled { nameserver := allSettings.DNS.PlaintextAddress.String() if allSettings.DNS.Enabled { @@ -209,9 +188,11 @@ func _main(background context.Context, args []string) int { restartOpenvpn := make(chan struct{}) restartUnbound := make(chan struct{}) restartPublicIP := make(chan struct{}) + restartTinyproxy := make(chan struct{}) openvpnDone := make(chan struct{}) unboundDone := make(chan struct{}) serverDone := make(chan struct{}) + tinyproxyDone := make(chan struct{}) openvpnLooper := openvpn.NewLooper(ovpnConf, allSettings.OpenVPN, logger, streamMerger, fatalOnError, uid, gid) // wait for restartOpenvpn @@ -225,6 +206,13 @@ func _main(background context.Context, args []string) int { go publicIPLooper.Run(ctx, restartPublicIP) go publicIPLooper.RunRestartTicker(ctx, restartPublicIP) + tinyproxyLooper := tinyproxy.NewLooper(tinyProxyConf, firewallConf, allSettings.TinyProxy, logger, streamMerger, uid, gid) + go tinyproxyLooper.Run(ctx, restartTinyproxy, tinyproxyDone) + + if allSettings.TinyProxy.Enabled { + <-restartTinyproxy + } + go func() { first := true var restartTickerContext context.Context diff --git a/internal/tinyproxy/loop.go b/internal/tinyproxy/loop.go new file mode 100644 index 00000000..67e75b12 --- /dev/null +++ b/internal/tinyproxy/loop.go @@ -0,0 +1,106 @@ +package tinyproxy + +import ( + "context" + "time" + + "github.com/qdm12/golibs/command" + "github.com/qdm12/golibs/logging" + "github.com/qdm12/private-internet-access-docker/internal/constants" + "github.com/qdm12/private-internet-access-docker/internal/firewall" + "github.com/qdm12/private-internet-access-docker/internal/settings" +) + +type Looper interface { + Run(ctx context.Context, restart <-chan struct{}, done chan<- struct{}) +} + +type looper struct { + conf Configurator + firewallConf firewall.Configurator + settings settings.TinyProxy + logger logging.Logger + streamMerger command.StreamMerger + uid int + gid int +} + +func (l *looper) logAndWait(err error) { + l.logger.Error(err) + l.logger.Info("retrying in 1 minute") + time.Sleep(time.Minute) +} + +func NewLooper(conf Configurator, firewallConf firewall.Configurator, settings settings.TinyProxy, + logger logging.Logger, streamMerger command.StreamMerger, uid, gid int) Looper { + return &looper{ + conf: conf, + firewallConf: firewallConf, + settings: settings, + logger: logger.WithPrefix("tinyproxy: "), + streamMerger: streamMerger, + uid: uid, + gid: gid, + } +} + +func (l *looper) Run(ctx context.Context, restart <-chan struct{}, done chan<- struct{}) { + select { + case <-restart: + case <-ctx.Done(): + close(done) + return + } + for { + err := l.conf.MakeConf( + l.settings.LogLevel, + l.settings.Port, + l.settings.User, + l.settings.Password, + l.uid, + l.gid) + if err != nil { + l.logAndWait(err) + continue + } + err = l.firewallConf.AllowAnyIncomingOnPort(ctx, l.settings.Port) + // TODO remove firewall rule on exit below + if err != nil { + l.logger.Error(err) + } + tinyproxyCtx, tinyproxyCancel := context.WithCancel(ctx) + stream, waitFn, err := l.conf.Start(tinyproxyCtx) + if err != nil { + tinyproxyCancel() + l.logAndWait(err) + continue + } + go l.streamMerger.Merge(tinyproxyCtx, stream, + command.MergeName("tinyproxy"), command.MergeColor(constants.ColorTinyproxy())) + waitError := make(chan error) + go func() { + err := waitFn() // blocking + if tinyproxyCtx.Err() != context.Canceled { + waitError <- err + } + }() + select { + case <-ctx.Done(): + l.logger.Warn("context canceled: exiting loop") + tinyproxyCancel() + close(waitError) + close(done) + return + case <-restart: // triggered restart + l.logger.Info("restarting") + tinyproxyCancel() + close(waitError) + case err := <-waitError: // unexpected error + l.logger.Warn(err) + l.logger.Info("restarting") + tinyproxyCancel() + close(waitError) + time.Sleep(time.Second) + } + } +}