diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 0317553..8a550fb 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -17,6 +17,7 @@ import ( "github.com/panjf2000/ants/v2" ) +// Proxy is an MTPROTO proxy structure. type Proxy struct { ctx context.Context ctxCancel context.CancelFunc @@ -37,10 +38,13 @@ type Proxy struct { logger Logger } +// DomainFrontingAddress returns a host:port pair for a fronting domain. func (p *Proxy) DomainFrontingAddress() string { return net.JoinHostPort(p.secret.Host, strconv.Itoa(p.domainFrontingPort)) } +// ServeConn serves a connection. We do not check IP blocklist and +// concurrency limit here. func (p *Proxy) ServeConn(conn net.Conn) { p.streamWaitGroup.Add(1) defer p.streamWaitGroup.Done() @@ -86,6 +90,7 @@ func (p *Proxy) ServeConn(conn net.Conn) { } } +// Serve starts a proxy on a given listener. func (p *Proxy) Serve(listener net.Listener) error { p.streamWaitGroup.Add(1) defer p.streamWaitGroup.Done() @@ -93,7 +98,12 @@ func (p *Proxy) Serve(listener net.Listener) error { for { conn, err := listener.Accept() if err != nil { - return fmt.Errorf("cannot accept a new connection: %w", err) + select { + case <-p.ctx.Done(): + return nil + default: + return fmt.Errorf("cannot accept a new connection: %w", err) + } } ipAddr := conn.RemoteAddr().(*net.TCPAddr).IP @@ -117,15 +127,11 @@ func (p *Proxy) Serve(listener net.Listener) error { logger.Info("connection was concurrency limited") p.eventStream.Send(p.ctx, NewEventConcurrencyLimited()) } - - select { - case <-p.ctx.Done(): - return p.ctx.Err() - default: - } } } +// Shutdown 'gracefully' shutdowns all connections. Please remember that +// it does not close an underlying listener. func (p *Proxy) Shutdown() { p.ctxCancel() p.streamWaitGroup.Wait() @@ -262,6 +268,7 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) { } } +// NewProxy makes a new proxy instance. func NewProxy(opts ProxyOpts) (*Proxy, error) { if err := opts.valid(); err != nil { return nil, fmt.Errorf("invalid settings: %w", err) diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index c7bff07..8da37f8 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -2,20 +2,89 @@ package mtglib import "time" +// ProxyOpts is a structure with settings to mtg proxy. +// +// This is not required per se, but this is to shorten function +// signature and give an ability to conveniently provide default values. type ProxyOpts struct { - Secret Secret - Network Network - AntiReplayCache AntiReplayCache - TimeAttackDetector TimeAttackDetector - IPBlocklist IPBlocklist - EventStream EventStream - Logger Logger + // Secret defines a secret which should be used by a proxy. + // + // This is a mandatory setting. + Secret Secret - BufferSize uint - Concurrency uint + // Network defines a network instance which should be used for all + // network communications made by proxies. + // + // This is a mandatory setting. + Network Network + + // AntiReplayCache defines an instance of antireplay cache. + // + // This is a mandatory setting. + AntiReplayCache AntiReplayCache + + // TimeAttackDetector defines an instance of timeattack detector. + // + // This is a mandatory setting. + TimeAttackDetector TimeAttackDetector + + // IPBlocklist defines an instance of IP blocklist. + // + // This is a mandatory setting. + IPBlocklist IPBlocklist + + // EventStream defines an instance of event stream. + // + // This ia a mandatory setting. + EventStream EventStream + + // Logger defines an instance of the logger. + // + // This is a mandatory setting. + Logger Logger + + // BufferSize is a size of the copy buffer in bytes. + // + // Please remember that we multiply this number in 2, because when + // we relay between proxies, we have to create 2 intermediate + // buffers: to and from. + // + // This is an optional setting. + BufferSize uint + + // Concurrency is a size of the worker pool for connection management. + // + // If we have more connections than this number, they are going to be + // rejected. + // + // This is an optional setting. + Concurrency uint + + // DomainFrontingPort is a port we use to connect to a fronting + // domain. + // + // This is required because secret does not specify a port. It + // specifies a hostname only. + // + // This is an optional setting. DomainFrontingPort uint - IdleTimeout time.Duration - PreferIP string + + // IdleTimeout is a timeout for relay when we have to break a + // stream. + // + // This is a timeout for any activity. So, if we have any message + // which will pass to either direction, a timer is reset. If we have + // no any reads or writes for this timeout, a connection will be + // aborted. + // + // This is an optional setting. + IdleTimeout time.Duration + + // PreferIP defines an IP connectivity preference. Valid values are: + // 'prefer-ipv4', 'prefer-ipv6', 'only-ipv4', 'only-ipv6'. + // + // This is an optional setting. + PreferIP string } func (p ProxyOpts) valid() error {