diff --git a/antireplay/init.go b/antireplay/init.go index eb4eeb1..1005916 100644 --- a/antireplay/init.go +++ b/antireplay/init.go @@ -1,17 +1,17 @@ -// Antireplay package has cache implementations that are effective -// against replay attacks. +// Antireplay package has cache implementations that are effective against +// replay attacks. // -// To understand more about replay attacks, please read documentation -// for mtglib.AntiReplayCache interface. This package has a list of some +// To understand more about replay attacks, please read documentation for +// [mtglib.AntiReplayCache] interface. This package has a list of some // implementations of this interface. package antireplay const ( - // DefaultStableBloomFilterMaxSize is a recommended byte size for a - // stable bloom filter. + // DefaultStableBloomFilterMaxSize is a recommended byte size for a stable + // bloom filter. DefaultStableBloomFilterMaxSize = 1024 * 1024 // 1MiB - // DefaultStableBloomFilterErrorRate is a recommended default error - // rate for a stable bloom filter. + // DefaultStableBloomFilterErrorRate is a recommended default error rate for a + // stable bloom filter. DefaultStableBloomFilterErrorRate = 0.001 ) diff --git a/antireplay/noop.go b/antireplay/noop.go index e4e7cf4..6ca2450 100644 --- a/antireplay/noop.go +++ b/antireplay/noop.go @@ -6,9 +6,8 @@ type noop struct{} func (n noop) SeenBefore(_ []byte) bool { return false } -// NewNoop returns an implementation that does nothing. A corresponding -// method always returns false, so this cache accepts everything you -// pass to it. +// NewNoop returns an implementation that does nothing. A corresponding method +// always returns false, so this cache accepts everything you pass to it. func NewNoop() mtglib.AntiReplayCache { return noop{} } diff --git a/antireplay/stable_bloom_filter.go b/antireplay/stable_bloom_filter.go index 61f9934..96a10ce 100644 --- a/antireplay/stable_bloom_filter.go +++ b/antireplay/stable_bloom_filter.go @@ -20,19 +20,19 @@ func (s *stableBloomFilter) SeenBefore(digest []byte) bool { return s.filter.TestAndAdd(digest) } -// NewStableBloomFilter returns an implementation of AntiReplayCache -// based on stable bloom filter. +// NewStableBloomFilter returns an implementation of AntiReplayCache based on +// stable bloom filter. // // http://webdocs.cs.ualberta.ca/~drafiei/papers/DupDet06Sigmod.pdf // -// The basic idea of a stable bloom filter is quite simple: each time -// when you set a new element, you randomly reset P elements. There is a -// hardcore math which proves that if you choose this P correctly, you -// can maintain the same error rate for a stream of elements. +// The basic idea of a stable bloom filter is quite simple: each time when you +// set a new element, you randomly reset P elements. There is a hardcore math +// which proves that if you choose this P correctly, you can maintain the same +// error rate for a stream of elements. // // byteSize is the number of bytes you want to give to a bloom filter. -// errorRate is desired false-positive error rate. If you want to use -// default values, please pass 0 for byteSize and <0 for errorRate. +// errorRate is desired false-positive error rate. If you want to use default +// values, please pass 0 for byteSize and <0 for errorRate. func NewStableBloomFilter(byteSize uint, errorRate float64) mtglib.AntiReplayCache { if byteSize == 0 { byteSize = DefaultStableBloomFilterMaxSize diff --git a/essentials/conns.go b/essentials/conns.go index 1b3cbf0..5b42efe 100644 --- a/essentials/conns.go +++ b/essentials/conns.go @@ -5,19 +5,19 @@ import ( "net" ) -// CloseableReader is a reader interface that can close its reading end. +// CloseableReader is an [io.Reader] interface that can close its reading end. type CloseableReader interface { io.Reader CloseRead() error } -// CloseableWriter is a writer that can close its writing end. +// CloseableWriter is an [io.Writer] that can close its writing end. type CloseableWriter interface { io.Writer CloseWrite() error } -// Conn is an extension of net.Conn that can close its ends. This mostly +// Conn is an extension of [net.Conn] that can close its ends. This mostly // implies TCP connections. type Conn interface { net.Conn diff --git a/events/event_stream.go b/events/event_stream.go index c847bf2..551910a 100644 --- a/events/event_stream.go +++ b/events/event_stream.go @@ -9,7 +9,7 @@ import ( "github.com/OneOfOne/xxhash" ) -// EventStream is a default implementation of the mtglib.EventStream +// EventStream is a default implementation of the [mtglib.EventStream] // interface. // // EventStream manages a set of goroutines, observers. Main diff --git a/events/init.go b/events/init.go index 94804dc..6d54116 100644 --- a/events/init.go +++ b/events/init.go @@ -1,19 +1,19 @@ // Events has a default implementations of EventStream for mtglib. // -// Please see documentation for mtglib.EventStream interface to get an -// idea of such an abstraction. This package has implementations for the -// default event stream. +// Please see documentation for [mtglib.EventStream] interface to get an idea +// of such an abstraction. This package has implementations for the default +// event stream. // -// Default event stream has a list of its own concepts. First, all it -// does is a routing of messages to known observers. It takes an event, -// defines its type and pass this message to a method of the observer. +// Default event stream has a list of its own concepts. First, all it does is a +// routing of messages to known observers. It takes an event, defines its type +// and pass this message to a method of the observer. // -// There might be many observers, but default event stream has a -// guarantee though. It uses StreamID as a sharding key and guarantees -// that a message with the same StreamID will be devlivered to the same -// observer instance. So, each producer is guarateed to get all relevant -// messages related to the same session. It is not possible that it will -// get EventFinish if it has not seen EventStart for that session yet. +// There might be many observers, but default event stream has a guarantee +// though. It uses StreamID as a sharding key and guarantees that a message +// with the same StreamID will be devlivered to the same observer instance. So, +// each producer is guarateed to get all relevant messages related to the same +// session. It is not possible that it will get EventFinish if it has not seen +// EventStart for that session yet. package events import "github.com/9seconds/mtg/v2/mtglib" @@ -21,10 +21,10 @@ import "github.com/9seconds/mtg/v2/mtglib" // Observer is an instance that listens for the incoming events. // // As it is said in the package description, the default event stream -// guarantees that all events with the same StreamID are going to be -// routed to the same instance of the observer. So, there is no need -// to synchronize information about streams between many observers -// instances, they can have their local storage. +// guarantees that all events with the same StreamID are going to be routed to +// the same instance of the observer. So, there is no need to synchronize +// information about streams between many observers instances, they can have +// their local storage. type Observer interface { // EventStart reacts on incoming mtglib.EventStart event. EventStart(mtglib.EventStart) @@ -65,8 +65,8 @@ type Observer interface { // ObserverFactory creates a new instance of the observer. // -// Default event stream creates a small set of goroutines to manage -// incoming messages. Each message is routed to an appropriate observer -// based on a sharding key, stream id. So, it is possible that an -// instance of mtg will have many observer instances, not a single one. +// Default event stream creates a small set of goroutines to manage incoming +// messages. Each message is routed to an appropriate observer based on a +// sharding key, stream id. So, it is possible that an instance of mtg will +// have many observer instances, not a single one. type ObserverFactory func() Observer diff --git a/ipblocklist/files/http.go b/ipblocklist/files/http.go index 6a60ede..2162707 100644 --- a/ipblocklist/files/http.go +++ b/ipblocklist/files/http.go @@ -40,6 +40,8 @@ func (h httpFile) String() string { return h.url } +// NewHTTP returns a file abstraction for HTTP/HTTPS endpoint. You also need to +// provide a valid instance of [http.Client] to access it. func NewHTTP(client *http.Client, endpoint string) (File, error) { if client == nil { return nil, ErrBadHTTPClient diff --git a/ipblocklist/files/init.go b/ipblocklist/files/init.go index 97570af..5876ecc 100644 --- a/ipblocklist/files/init.go +++ b/ipblocklist/files/init.go @@ -6,9 +6,16 @@ import ( "io" ) +// ErrBadHTTPClient is returned if given HTTP client is initialized +// incorrectly. var ErrBadHTTPClient = errors.New("incorrect http client") +// File is an abstraction for a entity that can be opened in some context. type File interface { + // Open returns an readable entity for a file. It is important to not forget + // to close it after the usage. Open(context.Context) (io.ReadCloser, error) + + // String returns a short text description for the file String() string } diff --git a/ipblocklist/files/local.go b/ipblocklist/files/local.go index 3cd08c7..9130028 100644 --- a/ipblocklist/files/local.go +++ b/ipblocklist/files/local.go @@ -19,6 +19,7 @@ func (l localFile) String() string { return l.path } +// NewLocal returns an openable File for a path on a local file system. func NewLocal(path string) (File, error) { if stat, err := os.Stat(path); os.IsNotExist(err) || stat.IsDir() || stat.Mode().Perm()&0o400 == 0 { return nil, fmt.Errorf("%s is not a readable file", path) diff --git a/ipblocklist/files/mem.go b/ipblocklist/files/mem.go index db23bef..ff62edf 100644 --- a/ipblocklist/files/mem.go +++ b/ipblocklist/files/mem.go @@ -19,6 +19,7 @@ func (m memFile) String() string { return "mem" } +// NewMem returns an openable file that is kept in RAM. func NewMem(networks []*net.IPNet) File { builder := strings.Builder{} diff --git a/ipblocklist/firehol.go b/ipblocklist/firehol.go index efe57f0..91f3a23 100644 --- a/ipblocklist/firehol.go +++ b/ipblocklist/firehol.go @@ -27,19 +27,19 @@ var ( // execute when ip list is updated. type FireholUpdateCallback func(context.Context, int) -// Firehol is IPBlocklist which uses lists from FireHOL: +// Firehol is [mtglib.IPBlocklist] which uses lists from FireHOL: // https://iplists.firehol.org/ // -// It can use both local files and remote URLs. This is not necessary -// that blocklists should be taken from this website, we expect only -// compatible formats here. +// It can use both local files and remote URLs. This is not necessary that +// blocklists should be taken from this website, we expect only compatible +// formats here. // // Example of the format: // -// # this is a comment -// # to ignore -// 127.0.0.1 # you can specify an IP -// 10.0.0.0/8 # or cidr +// # this is a comment +// # to ignore +// 127.0.0.1 # you can specify an IP +// 10.0.0.0/8 # or cidr type Firehol struct { ctx context.Context ctxCancel context.CancelFunc @@ -78,8 +78,7 @@ func (f *Firehol) Contains(ip net.IP) bool { // Run starts a background update process. // -// This is a blocking method so you probably want to run it in a -// goroutine. +// This is a blocking method so you probably want to run it in a goroutine. func (f *Firehol) Run(updateEach time.Duration) { if updateEach == 0 { updateEach = DefaultFireholUpdateEach @@ -211,8 +210,8 @@ func (f *Firehol) updateParseLine(text string) (*net.IPNet, error) { // NewFirehol creates a new instance of FireHOL IP blocklist. // -// This method does not start an update process so please execute Run -// when it is necessary. +// This method does not start an update process so please execute Run when it +// is necessary. func NewFirehol(logger mtglib.Logger, network mtglib.Network, downloadConcurrency uint, urls []string, @@ -244,6 +243,9 @@ func NewFirehol(logger mtglib.Logger, network mtglib.Network, return NewFireholFromFiles(logger, downloadConcurrency, blocklists, updateCallback) } +// NewFirehol creates a new instance of FireHOL IP blocklist. +// +// This method creates this instances from a given list of files. func NewFireholFromFiles(logger mtglib.Logger, downloadConcurrency uint, blocklists []files.File, diff --git a/ipblocklist/init.go b/ipblocklist/init.go index f5a06d3..b1b3cea 100644 --- a/ipblocklist/init.go +++ b/ipblocklist/init.go @@ -1,8 +1,8 @@ // Package ipblocklist contains default implementation of the -// IPBlocklist for mtg. +// [mtglib.IPBlocklist] for mtg. // -// Please check documentation for mtglib.IPBlocklist interface to get an -// idea of this abstraction. +// Please check documentation for [mtglib.IPBlocklist] interface to get an idea +// of this abstraction. package ipblocklist import "time" @@ -12,7 +12,7 @@ const ( // concurrent downloads of ip blocklists for Firehol. DefaultFireholDownloadConcurrency = 1 - // DefaultFireholUpdateEach defines a default time period when - // Firehol requests updates of the blocklists. + // DefaultFireholUpdateEach defines a default time period when Firehol + // requests updates of the blocklists. DefaultFireholUpdateEach = 6 * time.Hour ) diff --git a/ipblocklist/noop.go b/ipblocklist/noop.go index 40da20f..2d36317 100644 --- a/ipblocklist/noop.go +++ b/ipblocklist/noop.go @@ -13,8 +13,7 @@ func (n noop) Contains(ip net.IP) bool { return false } func (n noop) Run(updateEach time.Duration) {} func (n noop) Shutdown() {} -// NewNoop returns a dummy ipblocklist which allows all incoming -// connections. +// NewNoop returns a dummy ipblocklist which allows all incoming connections. func NewNoop() mtglib.IPBlocklist { return noop{} } diff --git a/logger/init.go b/logger/init.go index 8ff8ddc..da4ff40 100644 --- a/logger/init.go +++ b/logger/init.go @@ -1,14 +1,12 @@ -// Package logger has implementation of loggers for mtglib.Logger -// interface. +// Package logger has implementation of loggers for [mtglib.Logger] interface. // -// Please see a description of that interface to get some agreements -// which are used by mtglib. +// Please see a description of that interface to get some agreements which are +// used by mtglib. package logger -// StdLikeLogger is an interface which is close to log.Logger. This is -// commonly used by many 3pp tools. While mtglib itself does not need -// it, it is always a good idea to support it and have a transient end -// to end logging. +// StdLikeLogger is an interface which is close to [log.Logger]. This is +// commonly used by many 3pp tools. While mtglib itself does not need it, it is +// always a good idea to support it and have a transient end to end logging. type StdLikeLogger interface { Printf(format string, args ...interface{}) } diff --git a/mtglib/events.go b/mtglib/events.go index f1c5152..0a1aefd 100644 --- a/mtglib/events.go +++ b/mtglib/events.go @@ -29,13 +29,13 @@ type EventStart struct { RemoteIP net.IP } -// EventConnectedToDC is emitted when mtg proxy has connected to a -// Telegram server. +// EventConnectedToDC is emitted when mtg proxy has connected to a Telegram +// server. type EventConnectedToDC struct { eventBase - // RemoteIP is an IP address of the Telegram server proxy has been - // connected to. + // RemoteIP is an IP address of the Telegram server proxy has been connected + // to. RemoteIP net.IP // DC is an index of the datacenter proxy has been connected to. @@ -49,15 +49,15 @@ type EventTraffic struct { // Traffic is a count of bytes which were transmitted. Traffic uint - // IsRead defines if we _read_ or _write_ to connection. A rule of - // thumb is simple: EventTraffic is bound to a remote connection. Not - // to a client one, but either to Telegram or front domain one. + // IsRead defines if we _read_ or _write_ to connection. A rule of thumb is + // simple: EventTraffic is bound to a remote connection. Not to a client one, + // but either to Telegram or front domain one. // - // In the case of Telegram, isRead means that we've fetched some bytes - // from Telegram to send it to a client. + // In the case of Telegram, isRead means that we've fetched some bytes from + // Telegram to send it to a client. // - // In the case of the front domain, it means that we've fetched some - // bytes from this domain to send it to a client. + // In the case of the front domain, it means that we've fetched some bytes + // from this domain to send it to a client. IsRead bool } @@ -66,20 +66,20 @@ type EventFinish struct { eventBase } -// EventDomainFronting is emitted when we connect to a front domain -// instead of Telegram server. +// EventDomainFronting is emitted when we connect to a front domain instead of +// Telegram server. type EventDomainFronting struct { eventBase } -// EventConcurrencyLimited is emitted when connection was declined -// because of the concurrency limit of the worker pool. +// EventConcurrencyLimited is emitted when connection was declined because of +// the concurrency limit of the worker pool. type EventConcurrencyLimited struct { eventBase } -// EventIPBlocklisted is emitted when connection was declined because -// IP address was found in IP blocklist. +// EventIPBlocklisted is emitted when connection was declined because IP +// address was found in IP blocklist. type EventIPBlocklisted struct { eventBase diff --git a/mtglib/init.go b/mtglib/init.go index 0e00db6..35b5a71 100644 --- a/mtglib/init.go +++ b/mtglib/init.go @@ -1,20 +1,19 @@ // mtglib defines a package with MTPROTO proxy. // -// Since mtg itself is build as an example of how to work with mtglib, -// it worth to telling a couple of words about a project organization. +// Since mtg itself is build as an example of how to work with mtglib, it worth +// to telling a couple of words about a project organization. // -// A core object of the project is mtglib.Proxy. This is a proxy you -// expect: that one which you configure, set to serve on a listener -// and/or shutdown on application termination. +// A core object of the project is [mtglib.Proxy]. This is a proxy you expect: +// that one which you configure, set to serve on a listener and/or shutdown on +// application termination. // -// But it also has a core logic unrelated to Telegram per se: anti -// replay cache, network connectivity (who knows, maybe you want to have -// a native VMESS integration) and so on. +// But it also has a core logic unrelated to Telegram per se: anti replay +// cache, network connectivity (who knows, maybe you want to have a native +// VMESS integration) and so on. // -// You can supply such parts to a proxy with interfaces. The rest of -// the packages in mtg define some default implementations of these -// interfaces. But if you want to integrate it with, let say, influxdb, -// you can do it easily. +// You can supply such parts to a proxy with interfaces. The rest of the +// packages in mtg define some default implementations of these interfaces. But +// if you want to integrate it with, let say, influxdb, you can do it easily. package mtglib import ( @@ -28,42 +27,42 @@ import ( ) var ( - // ErrSecretEmpty is returned if you are trying to create a proxy - // but do not provide a secret. + // ErrSecretEmpty is returned if you are trying to create a proxy but do not + // provide a secret. ErrSecretEmpty = errors.New("secret is empty") - // ErrSecretInvalid is returned if you are trying to create a proxy - // but secret value is invalid (no host or payload are zeroes). + // ErrSecretInvalid is returned if you are trying to create a proxy but secret + // value is invalid (no host or payload are zeroes). ErrSecretInvalid = errors.New("secret is invalid") - // ErrNetworkIsNotDefined is returned if you are trying to create a - // proxy but network value is undefined. + // ErrNetworkIsNotDefined is returned if you are trying to create a proxy but + // network value is undefined. ErrNetworkIsNotDefined = errors.New("network is not defined") - // ErrAntiReplayCacheIsNotDefined is returned if you are trying to - // create a proxy but anti replay cache value is undefined. + // ErrAntiReplayCacheIsNotDefined is returned if you are trying to create a + // proxy but anti replay cache value is undefined. ErrAntiReplayCacheIsNotDefined = errors.New("anti-replay cache is not defined") - // ErrIPBlocklistIsNotDefined is returned if you are trying to - // create a proxy but ip blocklist instance is not defined. + // ErrIPBlocklistIsNotDefined is returned if you are trying to create a proxy + // but ip blocklist instance is not defined. ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined") - // ErrIPAllowlistIsNotDefined is returned if you are trying to - // create a proxy but ip allowlist instance is not defined. + // ErrIPAllowlistIsNotDefined is returned if you are trying to create a proxy + // but ip allowlist instance is not defined. ErrIPAllowlistIsNotDefined = errors.New("ip allowlist is not defined") - // ErrEventStreamIsNotDefined is returned if you are trying to create a - // proxy but event stream instance is not defined. + // ErrEventStreamIsNotDefined is returned if you are trying to create a proxy + // but event stream instance is not defined. ErrEventStreamIsNotDefined = errors.New("event stream is not defined") - // ErrLoggerIsNotDefined is returned if you are trying to - // create a proxy but logger is not defined. + // ErrLoggerIsNotDefined is returned if you are trying to create a proxy but + // logger is not defined. ErrLoggerIsNotDefined = errors.New("logger is not defined") ) const ( - // DefaultConcurrency is a default max count of simultaneously - // connected clients. + // DefaultConcurrency is a default max count of simultaneously connected + // clients. DefaultConcurrency = 4096 // DefaultBufferSize is a default size of a copy buffer. @@ -71,31 +70,29 @@ const ( // Deprecated: this setting no longer makes any effect. DefaultBufferSize = 16 * 1024 // 16 kib - // DefaultDomainFrontingPort is a default port (HTTPS) to connect to in - // case of probe-resistance activity. + // DefaultDomainFrontingPort is a default port (HTTPS) to connect to in case + // of probe-resistance activity. DefaultDomainFrontingPort = 443 - // DefaultIdleTimeout is a default timeout for closing a connection - // in case of idling. + // DefaultIdleTimeout is a default timeout for closing a connection in case of + // idling. // - // Deprecated: no longer in use because of changed TCP relay - // algorithm. + // Deprecated: no longer in use because of changed TCP relay algorithm. DefaultIdleTimeout = time.Minute - // DefaultTolerateTimeSkewness is a default timeout for time - // skewness on a faketls timeout verification. + // DefaultTolerateTimeSkewness is a default timeout for time skewness on a + // faketls timeout verification. DefaultTolerateTimeSkewness = 3 * time.Second - // DefaultPreferIP is a default value for Telegram IP connectivity - // preference. + // DefaultPreferIP is a default value for Telegram IP connectivity preference. DefaultPreferIP = "prefer-ipv6" - // SecretKeyLength defines a length of the secret bytes used - // by Telegram and a proxy. + // SecretKeyLength defines a length of the secret bytes used by Telegram and a + // proxy. SecretKeyLength = 16 - // ConnectionIDBytesLength defines a count of random bytes used to generate - // a stream/connection ids. + // ConnectionIDBytesLength defines a count of random bytes used to generate a + // stream/connection ids. ConnectionIDBytesLength = 16 // TCPRelayReadTimeout defines a max time period between two consecuitive @@ -104,81 +101,76 @@ const ( TCPRelayReadTimeout = 20 * time.Second ) -// Network defines a knowledge how to work with a network. It may sound -// fun but it encapsulates all the knowledge how to properly establish -// connections to remote hosts and configure HTTP clients. +// Network defines a knowledge how to work with a network. It may sound fun but +// it encapsulates all the knowledge how to properly establish connections to +// remote hosts and configure HTTP clients. // -// For example, if you want to use SOCKS5 proxy, you probably want to -// have all traffic routed to this proxy: telegram connections, http -// requests and so on. This knowledge is encapsulated into instances of -// such interface. +// For example, if you want to use SOCKS5 proxy, you probably want to have all +// traffic routed to this proxy: telegram connections, http requests and so on. +// This knowledge is encapsulated into instances of such interface. // // mtglib uses Network for: -// -// 1. Dialing to Telegram -// -// 2. Dialing to front domain -// -// 3. Doing HTTP requests (for example, for FireHOL ipblocklist). +// 1. Dialing to Telegram +// 2. Dialing to front domain +// 3. Doing HTTP requests (for example, for FireHOL ipblocklist). type Network interface { // Dial establishes context-free TCP connections. Dial(network, address string) (essentials.Conn, error) - // DialContext dials using a context. This is a preferrable - // way of establishing TCP connections. + // DialContext dials using a context. This is a preferrable way of + // establishing TCP connections. DialContext(ctx context.Context, network, address string) (essentials.Conn, error) - // MakeHTTPClient build an HTTP client with given dial function. If - // nothing is provided, then DialContext of this interface is going - // to be used. + // MakeHTTPClient build an HTTP client with given dial function. If nothing is + // provided, then DialContext of this interface is going to be used. MakeHTTPClient(func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client } -// AntiReplayCache is an interface that is used to detect replay attacks -// based on some traffic fingerprints. +// AntiReplayCache is an interface that is used to detect replay attacks based +// on some traffic fingerprints. // -// Replay attacks are probe attacks whose main goal is to identify if -// server software can be classified in some way. For example, if you -// send some HTTP request to a web server, then you can expect that this -// server will respond with HTTP response back. +// Replay attacks are probe attacks whose main goal is to identify if server +// software can be classified in some way. For example, if you send some HTTP +// request to a web server, then you can expect that this server will respond +// with HTTP response back. // -// There is a problem though. Let's imagine, that connection is -// encrypted. Let's imagine, that it is encrypted with some static key -// like ShadowSocks (https://shadowsocks.org/assets/whitepaper.pdf). -// In that case, in theory, if you repeat the same bytes, you can get -// the same responses. Let's imagine, that you've cracked the key. then -// if you send the same bytes, you can decrypt a response and see its -// structure. Based on its structure you can identify if this server is -// SOCKS5, MTPROTO proxy etc. +// There is a problem though. Let's imagine, that connection is encrypted. +// Let's imagine, that it is encrypted with some static key like [ShadowSocks]. +// In that case, in theory, if you repeat the same bytes, you can get the same +// responses. Let's imagine, that you've cracked the key. then if you send the +// same bytes, you can decrypt a response and see its structure. Based on its +// structure you can identify if this server is SOCKS5, MTPROTO proxy etc. // -// This is just one example, maybe not the best or not the most -// relevant. In real life, different organizations use such replay -// attacks to perform some reverse engineering of the proxy, do some -// statical analysis to identify server software. +// This is just one example, maybe not the best or not the most relevant. In +// real life, different organizations use such replay attacks to perform some +// reverse engineering of the proxy, do some statical analysis to identify +// server software. // -// There are many ways how to protect your proxy against them. One -// is domain fronting which is a core part of mtg. Another one is to -// collect some 'handshake fingerprints' and forbid duplication. +// There are many ways how to protect your proxy against them. One is domain +// fronting which is a core part of mtg. Another one is to collect some +// 'handshake fingerprints' and forbid duplication. // -// So, it one is sending the same byte flow right after you (or a couple -// of hours after), mtg should detect that and reject this connection -// (or redirect to fronting domain). +// So, it one is sending the same byte flow right after you (or a couple of +// hours after), mtg should detect that and reject this connection (or redirect +// to fronting domain). +// +// [ShadowSocks]: https://shadowsocks.org/assets/whitepaper.pdf type AntiReplayCache interface { - // Seen before checks if this set of bytes was observed before or - // not. If it is required to store this information somewhere else, - // then it has to do that. + // Seen before checks if this set of bytes was observed before or not. If it + // is required to store this information somewhere else, then it has to do + // that. SeenBefore(data []byte) bool } // IPBlocklist filters requests based on IP address. // -// If this filter has an IP address, then mtg closes a request without -// reading anything from a socket. It also does not give such request to -// a worker pool, so in worst cases you can expect that you invoke this -// object more frequent than defined proxy concurrency. +// If this filter has an IP address, then mtg closes a request without reading +// anything from a socket. It also does not give such request to a worker pool, +// so in worst cases you can expect that you invoke this object more frequent +// than defined proxy concurrency. type IPBlocklist interface { - // Contains checks if given IP address belongs to this blocklist If. - // it is, a connection is terminated . + // Contains checks if given IP address belongs to this blocklist If. it is, a + // connection is terminated . Contains(net.IP) bool // Run starts a background update procedure for a blocklist @@ -188,40 +180,35 @@ type IPBlocklist interface { Shutdown() } -// Event is a data structure which is populated during mtg request -// processing lifecycle. Each request popluates many events: +// Event is a data structure which is populated during mtg request processing +// lifecycle. Each request popluates many events: +// 1. Client connected +// 2. Request is finished +// 3. Connection to Telegram server is established // -// 1. Client connected -// -// 2. Request is finished -// -// 3. Connection to Telegram server is established -// -// and so on. All these events are data structures but all of them -// must conform the same interface. +// and so on. All these events are data structures but all of them must conform +// the same interface. type Event interface { - // StreamID returns an identifier of the stream, connection, - // request, you name it. All events within the same stream returns - // the same stream id. + // StreamID returns an identifier of the stream, connection, request, you name + // it. All events within the same stream returns the same stream id. StreamID() string // Timestamp returns a timestamp when this event was generated. Timestamp() time.Time } -// EventStream is an abstraction that accepts a set of events produced -// by mtg. Its main goal is to inject your logging or monitoring system. +// EventStream is an abstraction that accepts a set of events produced by mtg. +// Its main goal is to inject your logging or monitoring system. // -// The idea is simple. When mtg works, it emits a set of events during -// a lifecycle of the requestor: EventStart, EventFinish etc. mtg is a -// producer which puts these events into a stream. Responsibility of -// the stream is to deliver this event to consumers/observers. There -// might be many different observers (for example, you want to have both -// statsd and prometheus), mtg should know nothing about them. +// The idea is simple. When mtg works, it emits a set of events during a +// lifecycle of the requestor: EventStart, EventFinish etc. mtg is a producer +// which puts these events into a stream. Responsibility of the stream is to +// deliver this event to consumers/observers. There might be many different +// observers (for example, you want to have both statsd and prometheus), mtg +// should know nothing about them. type EventStream interface { - // Send delivers an event to observers. Given context has to be - // respected. If the context is closed, all blocking operations should - // be released ASAP. + // Send delivers an event to observers. Given context has to be respected. If + // the context is closed, all blocking operations should be released ASAP. // // It is possible that context is closed but the message is delivered. // EventStream implementations should solve this issue somehow. @@ -230,27 +217,26 @@ type EventStream interface { // Logger defines an interface of the logger used by mtglib. // -// Each logger has a name. It is possible to stack names to organize -// poor-man namespaces. Also, each logger must be able to bind -// parameters to avoid pushing them all the time. +// Each logger has a name. It is possible to stack names to organize poor-man +// namespaces. Also, each logger must be able to bind parameters to avoid +// pushing them all the time. // // Example // -// logger := SomeLogger{} -// logger = logger.BindStr("ip", net.IP{127, 0, 0, 1}) -// logger.Info("Hello") +// logger := SomeLogger{} logger = logger.BindStr("ip", net.IP{127, 0, 0, 1}) +// logger.Info("Hello") // -// In that case, ip is bound as a parameter. It is a great idea to -// put this parameter somewhere in a log message. +// In that case, ip is bound as a parameter. It is a great idea to put this +// parameter somewhere in a log message. // -// logger1 = logger.BindStr("param1", "11") -// logger2 = logger.BindInt("param2", 11) +// logger1 = logger.BindStr("param1", "11") logger2 = logger.BindInt("param2", +// 11) // // logger1 should see no param2 and vice versa, logger2 should not see param1 // If you attach a parameter to a logger, parents should not know about that. type Logger interface { - // Named returns a new logger with a bound name. Name chaining is - // allowed and appreciated. + // Named returns a new logger with a bound name. Name chaining is allowed and + // appreciated. Named(name string) Logger // BindInt binds new integer parameter to a new logger instance. @@ -268,22 +254,21 @@ type Logger interface { // Info puts a message about some normal situation. Info(msg string) - // InfoError puts a message about some normal situation but this - // situation is related to a given error. + // InfoError puts a message about some normal situation but this situation is + // related to a given error. InfoError(msg string, err error) - // Warning puts a message about some extraordinary situation - // worth to look at. + // Warning puts a message about some extraordinary situation worth to look at. Warning(msg string) - // WarningError puts a message about some extraordinary situation - // worth to look at. This situation is related to a given error. + // WarningError puts a message about some extraordinary situation worth to + // look at. This situation is related to a given error. WarningError(msg string, err error) // Debug puts a message useful for debugging only. Debug(msg string) - // Debug puts a message useful for debugging only. This message is - // related to a given error. + // Debug puts a message useful for debugging only. This message is related to + // a given error. DebugError(msg string, err error) } diff --git a/mtglib/proxy.go b/mtglib/proxy.go index e8d3b44..d8fc5d9 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -44,8 +44,8 @@ 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. +// ServeConn serves a connection. We do not check IP blocklist and concurrency +// limit here. func (p *Proxy) ServeConn(conn essentials.Conn) { p.streamWaitGroup.Add(1) defer p.streamWaitGroup.Done() @@ -138,8 +138,8 @@ func (p *Proxy) Serve(listener net.Listener) error { } } -// Shutdown 'gracefully' shutdowns all connections. Please remember that -// it does not close an underlying listener. +// Shutdown 'gracefully' shutdowns all connections. Please remember that it +// does not close an underlying listener. func (p *Proxy) Shutdown() { p.ctxCancel() p.streamWaitGroup.Wait() diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index c10463c..deb3fb0 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -4,16 +4,16 @@ 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. +// 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 defines a secret which should be used by a proxy. // // This is a mandatory setting. Secret Secret - // Network defines a network instance which should be used for all - // network communications made by proxies. + // Network defines a network instance which should be used for all network + // communications made by proxies. // // This is a mandatory setting. Network Network @@ -45,9 +45,8 @@ type ProxyOpts struct { // 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. + // 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. // @@ -62,22 +61,20 @@ type ProxyOpts struct { // This is an optional setting. Concurrency uint - // IdleTimeout is a timeout for relay when we have to break a - // stream. + // 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 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 - // TolerateTimeSkewness is a time boundary that defines a time - // range where faketls timestamp is acceptable. + // TolerateTimeSkewness is a time boundary that defines a time range where + // faketls timestamp is acceptable. // - // This means that if if you got a timestamp X, now is Y, then - // if |X-Y| < TolerateTimeSkewness, then you accept a packet. + // This means that if if you got a timestamp X, now is Y, then if |X-Y| < + // TolerateTimeSkewness, then you accept a packet. // // This is an optional setting. TolerateTimeSkewness time.Duration @@ -88,30 +85,29 @@ type ProxyOpts struct { // This is an optional setting. PreferIP string - // DomainFrontingPort is a port we use to connect to a fronting - // domain. + // 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 required because secret does not specify a port. It specifies a + // hostname only. // // This is an optional setting. DomainFrontingPort uint // AllowFallbackOnUnknownDC defines how proxy behaves if unknown DC was - // requested. If this setting is set to false, then such connection - // will be rejected. Otherwise, proxy will chose any DC. + // requested. If this setting is set to false, then such connection will be + // rejected. Otherwise, proxy will chose any DC. // - // Telegram is designed in a way that any DC can serve any request, - // the problem is a latency. + // Telegram is designed in a way that any DC can serve any request, the + // problem is a latency. // // This is an optional setting. AllowFallbackOnUnknownDC bool - // UseTestDCs defines if we have to connect to production or to staging - // DCs of Telegram. + // UseTestDCs defines if we have to connect to production or to staging DCs of + // Telegram. // - // This is required if you use mtglib as an integration library for - // your Telegram-related projects. + // This is required if you use mtglib as an integration library for your + // Telegram-related projects. // // This is an optional setting. UseTestDCs bool diff --git a/mtglib/secret.go b/mtglib/secret.go index eced181..a2d2c44 100644 --- a/mtglib/secret.go +++ b/mtglib/secret.go @@ -17,28 +17,27 @@ var secretEmptyKey [SecretKeyLength]byte // "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d". // Actually, this is a serialized datastructure of 2 parts: key and host. // -// ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d -// |-|-------------------------------|------------------------------------------- -// p key hostname +// ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d +// |-|-------------------------------|------------------------------------------- +// p key hostname // -// Serialized secret starts with 'ee'. Actually, in the past we also had -// 'dd' secrets and prefixless ones. But this is history. Currently, -// we do have only 'ee' secrets which mean faketls + protection from -// statistical attacks on a length. 'ee' is a byte 238 (0xee). +// Serialized secret starts with 'ee'. Actually, in the past we also had 'dd' +// secrets and prefixless ones. But this is history. Currently, we do have only +// 'ee' secrets which mean faketls + protection from statistical attacks on a +// length. 'ee' is a byte 238 (0xee). // -// After that, we have 16 bytes of the key. This is a random generated -// secret data of the proxy and this data is used to derive -// authentication schemas. These secrets are mixed into hmacs and sha256 -// checksums which are used to build AEAD ciphers for obfuscated2 -// protocol and ensure faketls handshake. +// After that, we have 16 bytes of the key. This is a random generated secret +// data of the proxy and this data is used to derive authentication schemas. +// These secrets are mixed into hmacs and sha256 checksums which are used to +// build AEAD ciphers for obfuscated2 protocol and ensure faketls handshake. // -// Host is a domain fronting hostname in latin1 (ASCII) encoding. This -// hostname should be used for SNI in faketls and MTG verifies it. Also, -// this is when mtg gets about a domain fronting hostname. +// Host is a domain fronting hostname in latin1 (ASCII) encoding. This hostname +// should be used for SNI in faketls and MTG verifies it. Also, this is when +// mtg gets about a domain fronting hostname. // -// Secrets can be serialized into 2 forms: hex and base64. If -// you decode both forms into bytes, you'll get the same byte array. -// Telegram clients nowadays accept all forms. +// Secrets can be serialized into 2 forms: hex and base64. If you decode both +// forms into bytes, you'll get the same byte array. Telegram clients nowadays +// accept all forms. type Secret struct { // Key is a set of bytes used for traffic authentication. Key [SecretKeyLength]byte diff --git a/network/init.go b/network/init.go index a1426b3..52831c2 100644 --- a/network/init.go +++ b/network/init.go @@ -1,20 +1,16 @@ // Network contains a default implementation of the network. // -// Please see mtglib.Network interface to get some basic idea behind -// this abstraction. +// Please see [mtglib.Network] interface to get some basic idea behind this +// abstraction. // // Some notable feature of this implementation: // -// 1. It detaches dialer from a network. Dialer is something which -// implements a real dialer and network completes it with more higher -// level details. -// -// 2. It uses only TCP connections. Even for DNS it uses DNS-Over-HTTPS -// -// 3. It has some simple implementation of DNS cache which is good -// enough for our purpose. -// -// 4. It sets uses SO_REUSEPORT port if applicable. +// 1. It detaches dialer from a network. Dialer is something which implements a +// real dialer and network completes it with more higher level details. +// 2. It uses only TCP connections. Even for DNS it uses DNS-Over-HTTPS +// 3. It has some simple implementation of DNS cache which is good enough for +// our purpose. +// 4. It sets uses SO_REUSEPORT port if applicable. package network import ( @@ -26,53 +22,47 @@ import ( ) const ( - // DefaultTimeout is a default timeout for establishing TCP - // connection. + // DefaultTimeout is a default timeout for establishing TCP connection. DefaultTimeout = 10 * time.Second - // DefaultHTTPTimeout defines a default timeout for making HTTP - // request. + // DefaultHTTPTimeout defines a default timeout for making HTTP request. DefaultHTTPTimeout = 10 * time.Second // Deprecated: // - // DefaultBufferSize defines a TCP buffer size. Both read and write, so - // for real size, please multiply this number by 2. + // DefaultBufferSize defines a TCP buffer size. Both read and write, so for + // real size, please multiply this number by 2. DefaultBufferSize = 16 * 1024 // 16 kib - // DefaultTCPKeepAlivePeriod defines a time period between 2 - // consequitive probes. + // DefaultTCPKeepAlivePeriod defines a time period between 2 consequitive + // probes. DefaultTCPKeepAlivePeriod = 10 * time.Second - // ProxyDialerOpenThreshold is used for load balancing SOCKS5 dialer - // only. + // ProxyDialerOpenThreshold is used for load balancing SOCKS5 dialer only. // - // This dialer uses circuit breaker with of 3 stages: OPEN, - // HALF_OPEN and CLOSED. If state is CLOSED, all requests go in - // a normal mode. If you get more that ProxyDialerOpenThreshold - // errors, circuit breaker goes into OPEN mode. + // This dialer uses circuit breaker with of 3 stages: OPEN, HALF_OPEN and + // CLOSED. If state is CLOSED, all requests go in a normal mode. If you get + // more that ProxyDialerOpenThreshold errors, circuit breaker goes into OPEN + // mode. // - // When circuit breaker is in OPEN mode, it forbids all request to - // a given proxy. But after ProxyDialerHalfOpenTimeout it gives a - // second chance and opens an access for a SINGLE request. If this - // request success, then circuit breaker closes, otherwise opens - // again. + // When circuit breaker is in OPEN mode, it forbids all request to a given + // proxy. But after ProxyDialerHalfOpenTimeout it gives a second chance and + // opens an access for a SINGLE request. If this request success, then circuit + // breaker closes, otherwise opens again. // // When circuit breaker is closed, it clears an error states each // ProxyDialerResetFailuresTimeout. ProxyDialerOpenThreshold = 5 - // ProxyDialerHalfOpenTimeout defines a halfopen timeout for circuit - // breaker. + // ProxyDialerHalfOpenTimeout defines a halfopen timeout for circuit breaker. ProxyDialerHalfOpenTimeout = time.Minute - // ProxyDialerResetFailuresTimeout defines a timeout for resetting a - // failure. + // ProxyDialerResetFailuresTimeout defines a timeout for resetting a failure. ProxyDialerResetFailuresTimeout = 10 * time.Second - // DefaultDOHHostname defines a default IP address for DOH host. - // Since mtg is simple, please pass IP address here. We do not - // have bootstrap servers here embedded. + // DefaultDOHHostname defines a default IP address for DOH host. Since mtg is + // simple, please pass IP address here. We do not have bootstrap servers here + // embedded. DefaultDOHHostname = "9.9.9.9" // DNSTimeout defines a timeout for DNS queries. @@ -84,12 +74,12 @@ const ( ) var ( - // ErrCircuitBreakerOpened is returned when proxy is being accessed - // but circuit breaker is opened. + // ErrCircuitBreakerOpened is returned when proxy is being accessed but + // circuit breaker is opened. ErrCircuitBreakerOpened = errors.New("circuit breaker is opened") - // ErrCannotDialWithAllProxies is returned when load balancing - // client is trying to access proxies but all of them are failed. + // ErrCannotDialWithAllProxies is returned when load balancing client is + // trying to access proxies but all of them are failed. ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies") ) diff --git a/network/load_balanced_socks5.go b/network/load_balanced_socks5.go index 37cbf28..f6d0a82 100644 --- a/network/load_balanced_socks5.go +++ b/network/load_balanced_socks5.go @@ -33,16 +33,14 @@ func (l loadBalancedSocks5Dialer) DialContext(ctx context.Context, network, addr return nil, ErrCannotDialWithAllProxies } -// NewLoadBalancedSocks5Dialer builds a new load balancing SOCKS5 -// dialer. +// NewLoadBalancedSocks5Dialer builds a new load balancing SOCKS5 dialer. // -// The main difference from one which is made by NewSocks5Dialer is that -// we actually have a list of these proxies. When dial is requested, -// any proxy is picked and used. If proxy fails for some reason, we try -// another one. +// The main difference from one which is made by NewSocks5Dialer is that we +// actually have a list of these proxies. When dial is requested, any proxy is +// picked and used. If proxy fails for some reason, we try another one. // -// So, it is mostly useful if you have some routes with proxies which -// are not always online or having buggy network. +// So, it is mostly useful if you have some routes with proxies which are not +// always online or having buggy network. func NewLoadBalancedSocks5Dialer(baseDialer Dialer, proxyURLs []*url.URL) (Dialer, error) { dialers := make([]Dialer, 0, len(proxyURLs)) diff --git a/network/network.go b/network/network.go index 6aecd6a..bdeeb1d 100644 --- a/network/network.go +++ b/network/network.go @@ -118,8 +118,8 @@ func (n *network) dnsResolve(protocol, address string) ([]string, error) { return ips, nil } -// NewNetwork assembles an mtglib.Network compatible structure -// based on a dialer and given params. +// NewNetwork assembles an mtglib.Network compatible structure based on a +// dialer and given params. // // It brings simple DNS cache and DNS-Over-HTTPS when necessary. func NewNetwork(dialer Dialer, diff --git a/network/socks5.go b/network/socks5.go index 459f64b..a8b3a99 100644 --- a/network/socks5.go +++ b/network/socks5.go @@ -136,8 +136,8 @@ func (s socks5Dialer) connect(conn io.ReadWriter, address string) error { return nil } -// NewSocks5Dialer build a new dialer from a given one (so, in theory -// you can chain here). Proxy parameters are passed with URI in a form of: +// NewSocks5Dialer build a new dialer from a given one (so, in theory you can +// chain here). Proxy parameters are passed with URI in a form of: // // socks5://[user:[password]]@host:port func NewSocks5Dialer(baseDialer Dialer, proxyURL *url.URL) (Dialer, error) { diff --git a/stats/init.go b/stats/init.go index 1bf33df..a2236e7 100644 --- a/stats/init.go +++ b/stats/init.go @@ -1,4 +1,4 @@ -// Stats package has implementations of events.Observers for different +// Stats package has implementations of [events.Observer] for different // monitoring systems. // // Observer is a consumer of events produced by mtg. Consumers, defined diff --git a/stats/prometheus.go b/stats/prometheus.go index eec64b5..593e57a 100644 --- a/stats/prometheus.go +++ b/stats/prometheus.go @@ -139,12 +139,11 @@ func (p prometheusProcessor) Shutdown() { } } -// PrometheusFactory is a factory of events.Observers which collect +// PrometheusFactory is a factory of [events.Observer] which collect // information in a format suitable for Prometheus. // -// This factory can also serve on a given listener. In that case it -// starts HTTP server with a single endpoint - a Prometheus-compatible -// scrape output. +// This factory can also serve on a given listener. In that case it starts HTTP +// server with a single endpoint - a Prometheus-compatible scrape output. type PrometheusFactory struct { httpServer *http.Server diff --git a/stats/statsd.go b/stats/statsd.go index 1c6a99f..b2d8811 100644 --- a/stats/statsd.go +++ b/stats/statsd.go @@ -147,13 +147,13 @@ func (s statsdProcessor) Shutdown() { } } -// StatsdFactory is a factory of events.Observers which dumps -// information to statsd. +// StatsdFactory is a factory of [events.Observer] which dumps information to +// statsd. // -// Please beware that we support ONLY UDP endpoints there. And this -// factory won't use mtglib.Network so it won't use a proxy if you -// provide any. If you need it, I would recommend starting a local -// statsd and route metrics further by features of the chosen server. +// Please beware that we support ONLY UDP endpoints there. And this factory +// won't use [mtglib.Network] so it won't use a proxy if you provide any. If +// you need it, I would recommend starting a local statsd and route metrics +// further by features of the chosen server. type StatsdFactory struct { client *statsd.Client } @@ -171,8 +171,7 @@ func (s StatsdFactory) Make() events.Observer { } } -// NewStatsd builds an events.ObserverFactory that sends events -// to statsd. +// NewStatsd builds an [events.ObserverFactory] that sends events to statsd. // // Valid tagFormats are 'datadog', 'influxdb' and 'graphite'. func NewStatsd(address string, log logger.StdLikeLogger,