From 29b5130c958df7466ad42a6857c5f277e095c276 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Mon, 9 Jul 2018 14:44:02 +0300 Subject: [PATCH] Fix lint errors --- Gopkg.lock | 2 +- client/client.go | 1 + client/direct.go | 8 +++++--- client/middle.go | 2 ++ config/config.go | 2 ++ main.go | 2 +- mtproto/connection_options.go | 13 ++++++++++--- mtproto/rpc/handshake_request.go | 4 ++++ mtproto/rpc/handshake_response.go | 6 ++++++ mtproto/rpc/nonce_request.go | 4 ++++ mtproto/rpc/nonce_response.go | 4 ++++ mtproto/rpc/proxy_request.go | 5 +++++ mtproto/rpc/rpc.go | 3 +++ proxy/proxy.go | 21 ++++++++++++--------- stats/channels.go | 9 +++++++-- stats/server.go | 3 ++- telegram/direct.go | 8 ++++---- telegram/middle.go | 17 ++++++++++------- telegram/middle_caller.go | 2 +- telegram/telegram.go | 1 + utils/read_current_data.go | 1 + utils/reverse_bytes.go | 1 + utils/uint24.go | 4 ++++ wrappers/blockcipher.go | 9 +++++++++ wrappers/conn.go | 25 ++++++++++++++++++------- wrappers/mtproto_abridged.go | 7 +++++++ wrappers/mtproto_cipher.go | 24 +++++++++++++----------- wrappers/mtproto_frame.go | 27 ++++++++++++++++++++++----- wrappers/mtproto_intermediate.go | 8 ++++++++ wrappers/mtproto_proxy.go | 6 ++++++ wrappers/streamcipher.go | 7 +++++++ wrappers/wrap.go | 26 ++++++++++++++++++++++++++ 32 files changed, 207 insertions(+), 55 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index cb43617..7a8f120 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -86,6 +86,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "312c9fb15085cbe9660443b15a07981990e1f70ec3ddfcce1b7e6cd5902307da" + inputs-digest = "c4fdd3664f683342ad0c2509f4a8bcfe5b267a6e8cdaf36f70d39536bbf89834" solver-name = "gps-cdcl" solver-version = 1 diff --git a/client/client.go b/client/client.go index 648fec2..fd503ff 100644 --- a/client/client.go +++ b/client/client.go @@ -8,4 +8,5 @@ import ( "github.com/9seconds/mtg/wrappers" ) +// Init defines common method for initializing client connections. type Init func(net.Conn, string, *config.Config) (wrappers.Wrap, *mtproto.ConnectionOpts, error) diff --git a/client/direct.go b/client/direct.go index 266071e..de8b66f 100644 --- a/client/direct.go +++ b/client/direct.go @@ -18,6 +18,8 @@ const ( writeBufferSize = 64 * 1024 ) +// DirectInit initializes client connection for proxy which connects to +// Telegram directly. func DirectInit(socket net.Conn, connID string, conf *config.Config) (wrappers.Wrap, *mtproto.ConnectionOpts, error) { tcpSocket := socket.(*net.TCPConn) if err := tcpSocket.SetNoDelay(false); err != nil { @@ -30,14 +32,14 @@ func DirectInit(socket net.Conn, connID string, conf *config.Config) (wrappers.W return nil, nil, errors.Annotate(err, "Cannot set write buffer size of client socket") } - socket.SetReadDeadline(time.Now().Add(handshakeTimeout)) + socket.SetReadDeadline(time.Now().Add(handshakeTimeout)) // nolint: errcheck frame, err := obfuscated2.ExtractFrame(socket) if err != nil { return nil, nil, errors.Annotate(err, "Cannot extract frame") } - socket.SetReadDeadline(time.Time{}) - conn := wrappers.NewConn(socket, connID, wrappers.ConnPurposeClient, conf.PublicIPv4, conf.PublicIPv6) + socket.SetReadDeadline(time.Time{}) // nolint: errcheck + conn := wrappers.NewConn(socket, connID, wrappers.ConnPurposeClient, conf.PublicIPv4, conf.PublicIPv6) obfs2, connOpts, err := obfuscated2.ParseObfuscated2ClientFrame(conf.Secret, frame) if err != nil { return nil, nil, errors.Annotate(err, "Cannot parse obfuscated frame") diff --git a/client/middle.go b/client/middle.go index b85deef..53bb64c 100644 --- a/client/middle.go +++ b/client/middle.go @@ -8,6 +8,8 @@ import ( "github.com/9seconds/mtg/wrappers" ) +// MiddleInit initializes client connection for proxy which has to +// support promoted channels, connect to Telegram middle proxies etc. func MiddleInit(socket net.Conn, connID string, conf *config.Config) (wrappers.Wrap, *mtproto.ConnectionOpts, error) { conn, opts, err := DirectInit(socket, connID, conf) if err != nil { diff --git a/config/config.go b/config/config.go index 27c11d0..f5fd97f 100644 --- a/config/config.go +++ b/config/config.go @@ -58,6 +58,8 @@ func (c *Config) StatAddr() string { return getAddr(c.StatsIP, c.StatsPort) } +// UseMiddleProxy defines if this proxy has to connect middle proxies +// which supports promoted channels or directly access Telegram. func (c *Config) UseMiddleProxy() bool { return len(c.AdTag) > 0 } diff --git a/main.go b/main.go index 2e06549..e7d8a16 100644 --- a/main.go +++ b/main.go @@ -114,7 +114,7 @@ func main() { atom, )) zap.ReplaceGlobals(logger) - defer logger.Sync() + defer logger.Sync() // nolint: errcheck printURLs(conf.GetURLs()) diff --git a/mtproto/connection_options.go b/mtproto/connection_options.go index 0285488..76d2bfe 100644 --- a/mtproto/connection_options.go +++ b/mtproto/connection_options.go @@ -11,8 +11,10 @@ import ( // by the user. type ConnectionType uint8 +// ConnectionProtocol is a type of IP protocol to use. type ConnectionProtocol uint8 +// Hacks is a simple structure to store flags for packet transmission. type Hacks struct { SimpleAck bool QuickAck bool @@ -24,9 +26,12 @@ type ConnectionOpts struct { DC int16 ConnectionType ConnectionType ConnectionProto ConnectionProtocol - ReadHacks Hacks - WriteHacks Hacks - ClientAddr *net.TCPAddr + // Read and Write means direction related to the client. + // ReadHacks are meant to be flushed on client read + // WriteHacks are meant to be flushed on client write. + ReadHacks Hacks + WriteHacks Hacks + ClientAddr *net.TCPAddr } // Different connection types which user requests from Telegram. @@ -36,6 +41,8 @@ const ( ConnectionTypeIntermediate ) +// ConnectionProtocol* define which connection protocols to use. +// ConnectionProtocolAny means that any is suitable. const ( ConnectionProtocolIPv4 ConnectionProtocol = 1 ConnectionProtocolIPv6 = ConnectionProtocolIPv4 << 1 diff --git a/mtproto/rpc/handshake_request.go b/mtproto/rpc/handshake_request.go index 924859d..acf3807 100644 --- a/mtproto/rpc/handshake_request.go +++ b/mtproto/rpc/handshake_request.go @@ -2,9 +2,12 @@ package rpc import "bytes" +// HandshakeRequest is the data type which is responsible for +// constructing of correct handshake request. type HandshakeRequest struct { } +// Bytes returns serialized handshake request. func (r *HandshakeRequest) Bytes() []byte { buf := &bytes.Buffer{} buf.Grow(len(TagHandshake) + len(HandshakeFlags) + len(HandshakeSenderPID) + len(HandshakePeerPID)) @@ -17,6 +20,7 @@ func (r *HandshakeRequest) Bytes() []byte { return buf.Bytes() } +// NewHandshakeRequest creates new HandshakeRequest instance. func NewHandshakeRequest() *HandshakeRequest { return &HandshakeRequest{} } diff --git a/mtproto/rpc/handshake_response.go b/mtproto/rpc/handshake_response.go index 40534d4..a8c522c 100644 --- a/mtproto/rpc/handshake_response.go +++ b/mtproto/rpc/handshake_response.go @@ -6,6 +6,8 @@ import ( "github.com/juju/errors" ) +// HandshakeResponse defines data structure which is used for storage of +// handshake response. type HandshakeResponse struct { Type []byte Flags []byte @@ -13,6 +15,7 @@ type HandshakeResponse struct { PeerPID []byte } +// Bytes returns a serialized handshake response. func (r *HandshakeResponse) Bytes() []byte { buf := &bytes.Buffer{} @@ -24,6 +27,7 @@ func (r *HandshakeResponse) Bytes() []byte { return buf.Bytes() } +// Valid checks that handshake response compliments request. func (r *HandshakeResponse) Valid(req *HandshakeRequest) error { if !bytes.Equal(r.Type, TagHandshake) { return errors.New("Unexpected handshake tag") @@ -35,6 +39,8 @@ func (r *HandshakeResponse) Valid(req *HandshakeRequest) error { return nil } +// NewHandshakeResponse constructs new handshake response from the given +// data. func NewHandshakeResponse(data []byte) (*HandshakeResponse, error) { if len(data) != 32 { return nil, errors.New("Incorrect handshake response length") diff --git a/mtproto/rpc/nonce_request.go b/mtproto/rpc/nonce_request.go index e4fb8a0..b714a9c 100644 --- a/mtproto/rpc/nonce_request.go +++ b/mtproto/rpc/nonce_request.go @@ -9,12 +9,15 @@ import ( "github.com/juju/errors" ) +// NonceRequest is the data type which contains all the data for correct +// nonce request. type NonceRequest struct { KeySelector []byte CryptoTS []byte Nonce []byte } +// Bytes returns serialized nonce request. func (r *NonceRequest) Bytes() []byte { buf := &bytes.Buffer{} @@ -27,6 +30,7 @@ func (r *NonceRequest) Bytes() []byte { return buf.Bytes() } +// NewNonceRequest builds new none request based on proxy secret. func NewNonceRequest(proxySecret []byte) (*NonceRequest, error) { nonce := make([]byte, 16) keySelector := make([]byte, 4) diff --git a/mtproto/rpc/nonce_response.go b/mtproto/rpc/nonce_response.go index 85c9383..f76bd44 100644 --- a/mtproto/rpc/nonce_response.go +++ b/mtproto/rpc/nonce_response.go @@ -6,6 +6,7 @@ import ( "github.com/juju/errors" ) +// NonceResponse is the data type which contains data of nonce response. type NonceResponse struct { NonceRequest @@ -13,6 +14,7 @@ type NonceResponse struct { Crypto []byte } +// Bytes returns serialized form of the nonce response. func (r *NonceResponse) Bytes() []byte { buf := &bytes.Buffer{} @@ -25,6 +27,7 @@ func (r *NonceResponse) Bytes() []byte { return buf.Bytes() } +// Valid checks that nonce response compliments nonce request. func (r *NonceResponse) Valid(req *NonceRequest) error { if !bytes.Equal(r.Type, TagNonce) { return errors.New("Unexpected RPC type") @@ -39,6 +42,7 @@ func (r *NonceResponse) Valid(req *NonceRequest) error { return nil } +// NewNonceResponse build new nonce response based on the given data. func NewNonceResponse(data []byte) (*NonceResponse, error) { if len(data) != 32 { return nil, errors.New("Unexpected message length") diff --git a/mtproto/rpc/proxy_request.go b/mtproto/rpc/proxy_request.go index 40ab3cf..9e2dacf 100644 --- a/mtproto/rpc/proxy_request.go +++ b/mtproto/rpc/proxy_request.go @@ -12,6 +12,8 @@ import ( "github.com/9seconds/mtg/mtproto" ) +// ProxyRequest is the data type for storing data required to compose +// RPC_PROXY_REQ request. type ProxyRequest struct { Flags proxyRequestFlags ConnectionID []byte @@ -21,6 +23,8 @@ type ProxyRequest struct { Options *mtproto.ConnectionOpts } +// MakeHeader makes RPC_PROXY_REQ header. We need only to append the +// data for it. func (r *ProxyRequest) MakeHeader(message []byte) (*bytes.Buffer, fmt.Stringer) { bufferLength := len(TagProxyRequest) + 4 + // len(flags) @@ -59,6 +63,7 @@ func (r *ProxyRequest) MakeHeader(message []byte) (*bytes.Buffer, fmt.Stringer) return buf, flags } +// NewProxyRequest build new ProxyRequest data structure. func NewProxyRequest(clientAddr, ownAddr *net.TCPAddr, opts *mtproto.ConnectionOpts, adTag []byte) (*ProxyRequest, error) { flags := proxyRequestFlagsHasAdTag | proxyRequestFlagsMagic | proxyRequestFlagsExtMode2 diff --git a/mtproto/rpc/rpc.go b/mtproto/rpc/rpc.go index bfb0555..5d242f5 100644 --- a/mtproto/rpc/rpc.go +++ b/mtproto/rpc/rpc.go @@ -1,10 +1,13 @@ package rpc +// SeqNo* is the number of the sequence which have special meaning for +// the Telegram. const ( SeqNoNonce = -2 SeqNoHandshake = -1 ) +// Different constants for RPC protocol var ( TagCloseExt = []byte{0xa2, 0x34, 0xb6, 0x5e} TagProxyAns = []byte{0x0d, 0xda, 0x03, 0x44} diff --git a/proxy/proxy.go b/proxy/proxy.go index 1f2df20..42f5d2b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -17,12 +17,14 @@ import ( "github.com/9seconds/mtg/wrappers" ) +// Proxy is a core of this program. type Proxy struct { clientInit client.Init tg telegram.Telegram conf *config.Config } +// Serve runs TCP proxy server. func (p *Proxy) Serve() error { lsock, err := net.Listen("tcp", p.conf.BindAddr()) if err != nil { @@ -43,7 +45,7 @@ func (p *Proxy) accept(conn net.Conn) { log := zap.S().With("connection_id", connID).Named("main") defer func() { - conn.Close() + conn.Close() // nolint: errcheck if err := recover(); err != nil { stats.NewCrash() @@ -58,7 +60,7 @@ func (p *Proxy) accept(conn net.Conn) { log.Errorw("Cannot initialize client connection", "error", err) return } - defer client.(io.Closer).Close() + defer client.(io.Closer).Close() // nolint: errcheck stats.ClientConnected(opts.ConnectionType, client.RemoteAddr()) defer stats.ClientDisconnected(opts.ConnectionType, client.RemoteAddr()) @@ -68,7 +70,7 @@ func (p *Proxy) accept(conn net.Conn) { log.Errorw("Cannot initialize server connection", "error", err) return } - defer server.(io.Closer).Close() + defer server.(io.Closer).Close() // nolint: errcheck wait := &sync.WaitGroup{} wait.Add(2) @@ -104,10 +106,10 @@ func (p *Proxy) getTelegramConn(opts *mtproto.ConnectionOpts, connID string) (wr return packetConn, nil } -func (p *Proxy) middlePipe(src wrappers.PacketReadCloser, dst wrappers.PacketWriteCloser, wait *sync.WaitGroup, hacks *mtproto.Hacks) { +func (p *Proxy) middlePipe(src wrappers.PacketReadCloser, dst io.WriteCloser, wait *sync.WaitGroup, hacks *mtproto.Hacks) { defer func() { - src.Close() - dst.Close() + src.Close() // nolint: errcheck + dst.Close() // nolint: errcheck wait.Done() }() @@ -127,10 +129,10 @@ func (p *Proxy) middlePipe(src wrappers.PacketReadCloser, dst wrappers.PacketWri } } -func (p *Proxy) directPipe(src wrappers.StreamReadCloser, dst wrappers.StreamWriteCloser, wait *sync.WaitGroup) { +func (p *Proxy) directPipe(src wrappers.StreamReadCloser, dst io.WriteCloser, wait *sync.WaitGroup) { defer func() { - src.Close() - dst.Close() + src.Close() // nolint: errcheck + dst.Close() // nolint: errcheck wait.Done() }() @@ -139,6 +141,7 @@ func (p *Proxy) directPipe(src wrappers.StreamReadCloser, dst wrappers.StreamWri } } +// NewProxy returns new proxy instance. func NewProxy(conf *config.Config) *Proxy { var clientInit client.Init var tg telegram.Telegram diff --git a/stats/channels.go b/stats/channels.go index c9bab87..ee2911b 100644 --- a/stats/channels.go +++ b/stats/channels.go @@ -21,8 +21,8 @@ var ( type connectionData struct { connectionType mtproto.ConnectionType - addr *net.TCPAddr connected bool + addr *net.TCPAddr } type trafficData struct { @@ -40,7 +40,7 @@ func crashManager() { } } -func connectionManager() { +func connectionManager() { // nolint: gocyclo for event := range connectionsChan { instance.mutex.RLock() @@ -111,10 +111,12 @@ func trafficManager() { } } +// NewCrash indicates new crash. func NewCrash() { crashesChan <- struct{}{} } +// ClientConnected indicates that new client was connected. func ClientConnected(connectionType mtproto.ConnectionType, addr *net.TCPAddr) { connectionsChan <- &connectionData{ connectionType: connectionType, @@ -123,6 +125,7 @@ func ClientConnected(connectionType mtproto.ConnectionType, addr *net.TCPAddr) { } } +// ClientDisconnected indicates that client was disconnected. func ClientDisconnected(connectionType mtproto.ConnectionType, addr *net.TCPAddr) { connectionsChan <- &connectionData{ connectionType: connectionType, @@ -131,6 +134,7 @@ func ClientDisconnected(connectionType mtproto.ConnectionType, addr *net.TCPAddr } } +// IngressTraffic accounts new ingress traffic. func IngressTraffic(traffic int) { trafficChan <- &trafficData{ traffic: traffic, @@ -138,6 +142,7 @@ func IngressTraffic(traffic int) { } } +// EgressTraffic accounts new ingress traffic. func EgressTraffic(traffic int) { trafficChan <- &trafficData{ traffic: traffic, diff --git a/stats/server.go b/stats/server.go index d0a13b9..a5369e4 100644 --- a/stats/server.go +++ b/stats/server.go @@ -13,6 +13,7 @@ import ( var instance *stats +// Start starts new statisitcs server. func Start(conf *config.Config) { log := zap.S().Named("stats") @@ -40,7 +41,7 @@ func Start(conf *config.Config) { } interm := map[string]interface{}{} - json.Unmarshal(first, &interm) + json.Unmarshal(first, &interm) // nolint: errcheck encoder := json.NewEncoder(w) encoder.SetEscapeHTML(false) diff --git a/telegram/direct.go b/telegram/direct.go index 749aa07..0656a50 100644 --- a/telegram/direct.go +++ b/telegram/direct.go @@ -28,11 +28,11 @@ var ( } ) -type DirectTelegram struct { +type directTelegram struct { baseTelegram } -func (t *DirectTelegram) Dial(connID string, connOpts *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) { +func (t *directTelegram) Dial(connID string, connOpts *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) { dc := connOpts.DC if dc < 0 { dc = -dc @@ -43,7 +43,7 @@ func (t *DirectTelegram) Dial(connID string, connOpts *mtproto.ConnectionOpts) ( return t.baseTelegram.dial(dc-1, connID, connOpts.ConnectionProto) } -func (t *DirectTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) { +func (t *directTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) { obfs2, frame := obfuscated2.MakeTelegramObfuscated2Frame(connOpts) if _, err := conn.Write(frame); err != nil { @@ -56,7 +56,7 @@ func (t *DirectTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.St // NewDirectTelegram returns Telegram instance which connects directly // to Telegram bypassing middleproxies. func NewDirectTelegram(conf *config.Config) Telegram { - return &DirectTelegram{baseTelegram{ + return &directTelegram{baseTelegram{ dialer: tgDialer{ Dialer: net.Dialer{Timeout: telegramDialTimeout}, conf: conf, diff --git a/telegram/middle.go b/telegram/middle.go index 7404e3d..1724bf0 100644 --- a/telegram/middle.go +++ b/telegram/middle.go @@ -1,6 +1,7 @@ package telegram import ( + "io" "net" "net/http" "sync" @@ -13,13 +14,13 @@ import ( "github.com/9seconds/mtg/wrappers" ) -type MiddleTelegram struct { +type middleTelegram struct { middleTelegramCaller conf *config.Config } -func (t *MiddleTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) { +func (t *middleTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) { rpcNonceConn := wrappers.NewMTProtoFrame(conn, rpc.SeqNoNonce) rpcNonceReq, err := t.sendRPCNonceRequest(rpcNonceConn) @@ -52,7 +53,7 @@ func (t *MiddleTelegram) Init(connOpts *mtproto.ConnectionOpts, conn wrappers.St return proxyConn, nil } -func (t *MiddleTelegram) sendRPCNonceRequest(conn wrappers.PacketWriter) (*rpc.NonceRequest, error) { +func (t *middleTelegram) sendRPCNonceRequest(conn io.Writer) (*rpc.NonceRequest, error) { rpcNonceReq, err := rpc.NewNonceRequest(t.proxySecret) if err != nil { return nil, errors.Annotate(err, "Cannot create RPC nonce request") @@ -64,7 +65,7 @@ func (t *MiddleTelegram) sendRPCNonceRequest(conn wrappers.PacketWriter) (*rpc.N return rpcNonceReq, nil } -func (t *MiddleTelegram) receiveRPCNonceResponse(conn wrappers.PacketReader, req *rpc.NonceRequest) (*rpc.NonceResponse, error) { +func (t *middleTelegram) receiveRPCNonceResponse(conn wrappers.PacketReader, req *rpc.NonceRequest) (*rpc.NonceResponse, error) { packet, err := conn.Read() if err != nil { return nil, errors.Annotate(err, "Cannot read RPC nonce response") @@ -81,7 +82,7 @@ func (t *MiddleTelegram) receiveRPCNonceResponse(conn wrappers.PacketReader, req return rpcNonceResp, nil } -func (t *MiddleTelegram) sendRPCHandshakeRequest(conn wrappers.PacketWriter) (*rpc.HandshakeRequest, error) { +func (t *middleTelegram) sendRPCHandshakeRequest(conn io.Writer) (*rpc.HandshakeRequest, error) { req := rpc.NewHandshakeRequest() if _, err := conn.Write(req.Bytes()); err != nil { return nil, errors.Annotate(err, "Cannot send RPC handshake request") @@ -90,7 +91,7 @@ func (t *MiddleTelegram) sendRPCHandshakeRequest(conn wrappers.PacketWriter) (*r return req, nil } -func (t *MiddleTelegram) receiveRPCHandshakeResponse(conn wrappers.PacketReader, req *rpc.HandshakeRequest) (*rpc.HandshakeResponse, error) { +func (t *middleTelegram) receiveRPCHandshakeResponse(conn wrappers.PacketReader, req *rpc.HandshakeRequest) (*rpc.HandshakeResponse, error) { packet, err := conn.Read() if err != nil { return nil, errors.Annotate(err, "Cannot read RPC handshake response") @@ -107,8 +108,10 @@ func (t *MiddleTelegram) receiveRPCHandshakeResponse(conn wrappers.PacketReader, return rpcHandshakeResp, nil } +// NewMiddleTelegram creates new instance of Telegram which works with +// middle proxies. func NewMiddleTelegram(conf *config.Config) Telegram { - tg := &MiddleTelegram{ + tg := &middleTelegram{ middleTelegramCaller: middleTelegramCaller{ baseTelegram: baseTelegram{ dialer: tgDialer{ diff --git a/telegram/middle_caller.go b/telegram/middle_caller.go index 832430a..bb5961a 100644 --- a/telegram/middle_caller.go +++ b/telegram/middle_caller.go @@ -104,7 +104,7 @@ func (t *middleTelegramCaller) getTelegramAddresses(url string) (map[int16][]str if err != nil { return nil, errors.Annotate(err, "Cannot access telegram server") } - defer resp.Body.Close() + defer resp.Body.Close() // nolint: errcheck scanner := bufio.NewScanner(resp.Body) data := map[int16][]string{} diff --git a/telegram/telegram.go b/telegram/telegram.go index 43f436d..b6b34ef 100644 --- a/telegram/telegram.go +++ b/telegram/telegram.go @@ -9,6 +9,7 @@ import ( "github.com/9seconds/mtg/wrappers" ) +// Telegram is an interface for different Telegram work modes. type Telegram interface { Dial(string, *mtproto.ConnectionOpts) (wrappers.StreamReadWriteCloser, error) Init(*mtproto.ConnectionOpts, wrappers.StreamReadWriteCloser) (wrappers.Wrap, error) diff --git a/utils/read_current_data.go b/utils/read_current_data.go index d66802b..284369c 100644 --- a/utils/read_current_data.go +++ b/utils/read_current_data.go @@ -4,6 +4,7 @@ import "io" const readCurrentDataBufferSize = 1024 + 1 // + 1 because telegram operates with blocks mod 4 +// ReadCurrentData reads all data from io.Reader which is ready to be read. func ReadCurrentData(src io.Reader) (rv []byte, err error) { buf := make([]byte, readCurrentDataBufferSize) n := readCurrentDataBufferSize diff --git a/utils/reverse_bytes.go b/utils/reverse_bytes.go index 447adbd..ab7cd5b 100644 --- a/utils/reverse_bytes.go +++ b/utils/reverse_bytes.go @@ -1,5 +1,6 @@ package utils +// ReverseBytes is a common slice reverser. func ReverseBytes(data []byte) []byte { dataLen := len(data) rv := make([]byte, dataLen) diff --git a/utils/uint24.go b/utils/uint24.go index 350f3d5..66c5f1b 100644 --- a/utils/uint24.go +++ b/utils/uint24.go @@ -1,11 +1,15 @@ package utils +// Uint24 is a replacement for the absent Go uint24 data type. +// This data type is little endian. type Uint24 [3]byte +// ToUint24 converts number to Uint24. func ToUint24(number uint32) Uint24 { return Uint24{byte(number), byte(number >> 8), byte(number >> 16)} } +// FromUint24 converts Uint24 to number. func FromUint24(number Uint24) uint32 { return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16) } diff --git a/wrappers/blockcipher.go b/wrappers/blockcipher.go index a4a7149..39fe7e8 100644 --- a/wrappers/blockcipher.go +++ b/wrappers/blockcipher.go @@ -12,6 +12,10 @@ import ( "github.com/juju/errors" ) +// BlockCipher is a stream writer which encrypts/decrypts blocks of data +// with AES CBC. This also is buffered reader. It means, that block +// reading is transparent for it, you can assume you are working with +// good old io.Reader. type BlockCipher struct { buf *bytes.Buffer @@ -63,22 +67,27 @@ func (b *BlockCipher) Write(p []byte) (int, error) { return b.conn.Write(encrypted) } +// Logger returns an instance of the logger for this wrapper. func (b *BlockCipher) Logger() *zap.SugaredLogger { return b.logger } +// LocalAddr returns local address of the underlying net.Conn. func (b *BlockCipher) LocalAddr() *net.TCPAddr { return b.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (b *BlockCipher) RemoteAddr() *net.TCPAddr { return b.conn.RemoteAddr() } +// Close closes underlying net.Conn. func (b *BlockCipher) Close() error { return b.conn.Close() } +// NewBlockCipher creates new instance of BlockCipher based on given data. func NewBlockCipher(conn StreamReadWriteCloser, encryptor, decryptor cipher.BlockMode) StreamReadWriteCloser { return &BlockCipher{ buf: &bytes.Buffer{}, diff --git a/wrappers/conn.go b/wrappers/conn.go index d770cb4..b8ec4e3 100644 --- a/wrappers/conn.go +++ b/wrappers/conn.go @@ -9,6 +9,9 @@ import ( "github.com/9seconds/mtg/stats" ) +// ConnPurpose is intented to be identifier of connection purpose. We +// sometimes want to treat client/telegram connection differently (for +// logging for example). type ConnPurpose uint8 func (c ConnPurpose) String() string { @@ -22,6 +25,7 @@ func (c ConnPurpose) String() string { return "" } +// ConnPurpose* define different connection types. const ( ConnPurposeClient = iota ConnPurposeTelegram @@ -32,6 +36,8 @@ const ( connTimeoutWrite = 5 * time.Minute ) +// Conn is a basic wrapper for net.Conn providing the most low-level +// logic and management as possible. type Conn struct { connID string conn net.Conn @@ -42,7 +48,7 @@ type Conn struct { } func (c *Conn) Write(p []byte) (int, error) { - c.conn.SetWriteDeadline(time.Now().Add(connTimeoutWrite)) + c.conn.SetWriteDeadline(time.Now().Add(connTimeoutWrite)) // nolint: errcheck n, err := c.conn.Write(p) c.logger.Debugw("Write to stream", "bytes", n, "error", err) @@ -52,7 +58,7 @@ func (c *Conn) Write(p []byte) (int, error) { } func (c *Conn) Read(p []byte) (int, error) { - c.conn.SetReadDeadline(time.Now().Add(connTimeoutRead)) + c.conn.SetReadDeadline(time.Now().Add(connTimeoutRead)) // nolint: errcheck n, err := c.conn.Read(p) c.logger.Debugw("Read from stream", "bytes", n, "error", err) @@ -61,11 +67,18 @@ func (c *Conn) Read(p []byte) (int, error) { return n, err } +// Close closes underlying net.Conn instance. func (c *Conn) Close() error { - defer c.logger.Debugw("Closed connection") + defer c.logger.Debugw("Close connection") return c.conn.Close() } +// Logger returns an instance of the logger for this wrapper. +func (c *Conn) Logger() *zap.SugaredLogger { + return c.logger +} + +// LocalAddr returns local address of the underlying net.Conn. func (c *Conn) LocalAddr() *net.TCPAddr { addr := c.conn.LocalAddr().(*net.TCPAddr) newAddr := *addr @@ -81,14 +94,12 @@ func (c *Conn) LocalAddr() *net.TCPAddr { return &newAddr } +// RemoteAddr returns remote address of the underlying net.Conn. func (c *Conn) RemoteAddr() *net.TCPAddr { return c.conn.RemoteAddr().(*net.TCPAddr) } -func (c *Conn) Logger() *zap.SugaredLogger { - return c.logger -} - +// NewConn initializes Conn wrapper for net.Conn. func NewConn(conn net.Conn, connID string, purpose ConnPurpose, publicIPv4, publicIPv6 net.IP) StreamReadWriteCloser { logger := zap.S().With( "connection_id", connID, diff --git a/wrappers/mtproto_abridged.go b/wrappers/mtproto_abridged.go index 2ed116f..e4775d0 100644 --- a/wrappers/mtproto_abridged.go +++ b/wrappers/mtproto_abridged.go @@ -18,6 +18,8 @@ const ( mtprotoAbridgedLargePacketLength = 16777216 // 256 ^ 3 ) +// MTProtoAbridged presents abridged connection between client and +// middle proxy. type MTProtoAbridged struct { conn StreamReadWriteCloser opts *mtproto.ConnectionOpts @@ -127,22 +129,27 @@ func (m *MTProtoAbridged) Write(p []byte) (int, error) { return 0, errors.Errorf("Packet is too big %d", len(p)) } +// Logger returns an instance of the logger for this wrapper. func (m *MTProtoAbridged) Logger() *zap.SugaredLogger { return m.logger } +// LocalAddr returns local address of the underlying net.Conn. func (m *MTProtoAbridged) LocalAddr() *net.TCPAddr { return m.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (m *MTProtoAbridged) RemoteAddr() *net.TCPAddr { return m.conn.RemoteAddr() } +// Close closes underlying net.Conn instance. func (m *MTProtoAbridged) Close() error { return m.conn.Close() } +// NewMTProtoAbridged creates new wrapper for abridged client connection. func NewMTProtoAbridged(conn StreamReadWriteCloser, opts *mtproto.ConnectionOpts) PacketReadWriteCloser { return &MTProtoAbridged{ conn: conn, diff --git a/wrappers/mtproto_cipher.go b/wrappers/mtproto_cipher.go index 72dad3f..bd26eb3 100644 --- a/wrappers/mtproto_cipher.go +++ b/wrappers/mtproto_cipher.go @@ -4,7 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" - "crypto/md5" + "crypto/md5" // nolint: gas "crypto/sha1" "encoding/binary" "net" @@ -13,21 +13,23 @@ import ( "github.com/9seconds/mtg/utils" ) -type CipherPurpose uint8 +type cipherPurpose uint8 const ( - CipherPurposeClient CipherPurpose = iota - CipherPurposeServer + cipherPurposeClient cipherPurpose = iota + cipherPurposeServer ) var emptyIP = [4]byte{0x00, 0x00, 0x00, 0x00} +// NewMiddleProxyCipher creates new block cipher to proxy<->telegram +// connection. func NewMiddleProxyCipher(conn StreamReadWriteCloser, req *rpc.NonceRequest, resp *rpc.NonceResponse, secret []byte) StreamReadWriteCloser { localAddr := conn.LocalAddr() remoteAddr := conn.RemoteAddr() - encKey, encIV := deriveKeys(CipherPurposeClient, req, resp, localAddr, remoteAddr, secret) - decKey, decIV := deriveKeys(CipherPurposeServer, req, resp, localAddr, remoteAddr, secret) + encKey, encIV := deriveKeys(cipherPurposeClient, req, resp, localAddr, remoteAddr, secret) + decKey, decIV := deriveKeys(cipherPurposeServer, req, resp, localAddr, remoteAddr, secret) enc, _ := makeEncrypterDecrypter(encKey, encIV) _, dec := makeEncrypterDecrypter(decKey, decIV) @@ -35,7 +37,7 @@ func NewMiddleProxyCipher(conn StreamReadWriteCloser, req *rpc.NonceRequest, res return NewBlockCipher(conn, enc, dec) } -func deriveKeys(purpose CipherPurpose, req *rpc.NonceRequest, resp *rpc.NonceResponse, client *net.TCPAddr, remote *net.TCPAddr, secret []byte) ([]byte, []byte) { +func deriveKeys(purpose cipherPurpose, req *rpc.NonceRequest, resp *rpc.NonceResponse, client *net.TCPAddr, remote *net.TCPAddr, secret []byte) ([]byte, []byte) { message := bytes.Buffer{} message.Write(resp.Nonce[:]) message.Write(req.Nonce[:]) @@ -54,9 +56,9 @@ func deriveKeys(purpose CipherPurpose, req *rpc.NonceRequest, resp *rpc.NonceRes message.Write(port[:]) switch purpose { - case CipherPurposeClient: + case cipherPurposeClient: message.WriteString("CLIENT") - case CipherPurposeServer: + case cipherPurposeServer: message.WriteString("SERVER") default: panic("Unexpected cipher purpose") @@ -75,11 +77,11 @@ func deriveKeys(purpose CipherPurpose, req *rpc.NonceRequest, resp *rpc.NonceRes message.Write(req.Nonce[:]) data := message.Bytes() - md5sum := md5.Sum(data[1:]) + md5sum := md5.Sum(data[1:]) // nolint: gas sha1sum := sha1.Sum(data) key := append(md5sum[:12], sha1sum[:]...) - iv := md5.Sum(data[2:]) + iv := md5.Sum(data[2:]) // nolint: gas return key, iv[:] } diff --git a/wrappers/mtproto_frame.go b/wrappers/mtproto_frame.go index 71ee084..58a523c 100644 --- a/wrappers/mtproto_frame.go +++ b/wrappers/mtproto_frame.go @@ -20,6 +20,18 @@ const ( var mtprotoFramePadding = []byte{0x04, 0x00, 0x00, 0x00} +// MTProtoFrame is a wrapper which converts written data to the MTProtoFrame. +// The format of the frame: +// +// [ MSGLEN(4) | SEQNO(4) | MSG(...) | CRC32(4) | PADDING(4*x) ] +// +// MSGLEN is the length of the message + len of seqno and msglen. +// SEQNO is the number of frame in the receive/send sequence. If client +// sends a message with SeqNo 18, it has to receive message with SeqNo 18. +// MSG is the data which has to be written +// CRC32 is the CRC32 checksum of MSGLEN + SEQNO + MSG +// PADDING is custom padding schema to complete frame length to such that +// len(frame) % 16 == 0 type MTProtoFrame struct { conn StreamReadWriteCloser logger *zap.SugaredLogger @@ -28,7 +40,7 @@ type MTProtoFrame struct { writeSeqNo int32 } -func (m *MTProtoFrame) Read() ([]byte, error) { +func (m *MTProtoFrame) Read() ([]byte, error) { // nolint: gocyclo buf := &bytes.Buffer{} sum := crc32.NewIEEE() writer := io.MultiWriter(buf, sum) @@ -60,7 +72,7 @@ func (m *MTProtoFrame) Read() ([]byte, error) { } var seqNo int32 - binary.Read(buf, binary.LittleEndian, &seqNo) + binary.Read(buf, binary.LittleEndian, &seqNo) // nolint: errcheck if seqNo != m.readSeqNo { return nil, errors.Errorf("Unexpected sequence number %d (wait for %d)", seqNo, m.readSeqNo) } @@ -96,12 +108,12 @@ func (m *MTProtoFrame) Write(p []byte) (int, error) { buf := &bytes.Buffer{} buf.Grow(messageLength + paddingLength) - binary.Write(buf, binary.LittleEndian, uint32(messageLength)) - binary.Write(buf, binary.LittleEndian, m.writeSeqNo) + binary.Write(buf, binary.LittleEndian, uint32(messageLength)) // nolint: errcheck + binary.Write(buf, binary.LittleEndian, m.writeSeqNo) // nolint: errcheck buf.Write(p) checksum := crc32.ChecksumIEEE(buf.Bytes()) - binary.Write(buf, binary.LittleEndian, checksum) + binary.Write(buf, binary.LittleEndian, checksum) // nolint: errcheck buf.Write(bytes.Repeat(mtprotoFramePadding, paddingLength/4)) m.logger.Debugw("Write MTProto frame", @@ -117,22 +129,27 @@ func (m *MTProtoFrame) Write(p []byte) (int, error) { return len(p), err } +// Logger returns an instance of the logger for this wrapper. func (m *MTProtoFrame) Logger() *zap.SugaredLogger { return m.logger } +// LocalAddr returns local address of the underlying net.Conn. func (m *MTProtoFrame) LocalAddr() *net.TCPAddr { return m.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (m *MTProtoFrame) RemoteAddr() *net.TCPAddr { return m.conn.RemoteAddr() } +// Close closes underlying net.Conn instance. func (m *MTProtoFrame) Close() error { return m.conn.Close() } +// NewMTProtoFrame creates new PacketWrapper for underlying connection. func NewMTProtoFrame(conn StreamReadWriteCloser, seqNo int32) PacketReadWriteCloser { return &MTProtoFrame{ conn: conn, diff --git a/wrappers/mtproto_intermediate.go b/wrappers/mtproto_intermediate.go index 5c29c79..ab0761c 100644 --- a/wrappers/mtproto_intermediate.go +++ b/wrappers/mtproto_intermediate.go @@ -14,6 +14,8 @@ import ( const mtprotoIntermediateQuickAckLength = 0x80000000 +// MTProtoIntermediate presents intermediate connection between client +// and Telegram. type MTProtoIntermediate struct { conn StreamReadWriteCloser opts *mtproto.ConnectionOpts @@ -88,22 +90,28 @@ func (m *MTProtoIntermediate) Write(p []byte) (int, error) { return m.conn.Write(append(length[:], p...)) } +// Logger returns an instance of the logger for this wrapper. func (m *MTProtoIntermediate) Logger() *zap.SugaredLogger { return m.logger } +// LocalAddr returns local address of the underlying net.Conn. func (m *MTProtoIntermediate) LocalAddr() *net.TCPAddr { return m.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (m *MTProtoIntermediate) RemoteAddr() *net.TCPAddr { return m.conn.RemoteAddr() } +// Close closes underlying net.Conn instance. func (m *MTProtoIntermediate) Close() error { return m.conn.Close() } +// NewMTProtoIntermediate creates new PacketWrapper for intermediate +// client connection. func NewMTProtoIntermediate(conn StreamReadWriteCloser, opts *mtproto.ConnectionOpts) PacketReadWriteCloser { return &MTProtoIntermediate{ conn: conn, diff --git a/wrappers/mtproto_proxy.go b/wrappers/mtproto_proxy.go index fbbb332..1babb95 100644 --- a/wrappers/mtproto_proxy.go +++ b/wrappers/mtproto_proxy.go @@ -12,6 +12,7 @@ import ( "github.com/9seconds/mtg/mtproto/rpc" ) +// MTProtoProxy is a wrapper which creates/reads RPC responses from Telegram. type MTProtoProxy struct { conn PacketReadWriteCloser req *rpc.ProxyRequest @@ -128,22 +129,27 @@ func (m *MTProtoProxy) Write(p []byte) (int, error) { return len(p), nil } +// Logger returns an instance of the logger for this wrapper. func (m *MTProtoProxy) Logger() *zap.SugaredLogger { return m.logger } +// LocalAddr returns local address of the underlying net.Conn. func (m *MTProtoProxy) LocalAddr() *net.TCPAddr { return m.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (m *MTProtoProxy) RemoteAddr() *net.TCPAddr { return m.conn.RemoteAddr() } +// Close closes underlying net.Conn instance. func (m *MTProtoProxy) Close() error { return m.conn.Close() } +// NewMTProtoProxy creates new RPC wrapper. func NewMTProtoProxy(conn PacketReadWriteCloser, connOpts *mtproto.ConnectionOpts, adTag []byte) (PacketReadWriteCloser, error) { req, err := rpc.NewProxyRequest(connOpts.ClientAddr, conn.LocalAddr(), connOpts, adTag) if err != nil { diff --git a/wrappers/streamcipher.go b/wrappers/streamcipher.go index da89535..b50a351 100644 --- a/wrappers/streamcipher.go +++ b/wrappers/streamcipher.go @@ -8,6 +8,8 @@ import ( "go.uber.org/zap" ) +// StreamCipher is a wrapper which encrypts/decrypts stream with AES-CTR +// (as a part of obfuscated2 protocol). type StreamCipher struct { encryptor cipher.Stream decryptor cipher.Stream @@ -32,22 +34,27 @@ func (s *StreamCipher) Write(p []byte) (int, error) { return s.conn.Write(encrypted) } +// Logger returns an instance of the logger for this wrapper. func (s *StreamCipher) Logger() *zap.SugaredLogger { return s.logger } +// LocalAddr returns local address of the underlying net.Conn. func (s *StreamCipher) LocalAddr() *net.TCPAddr { return s.conn.LocalAddr() } +// RemoteAddr returns remote address of the underlying net.Conn. func (s *StreamCipher) RemoteAddr() *net.TCPAddr { return s.conn.RemoteAddr() } +// Close closes underlying net.Conn instance. func (s *StreamCipher) Close() error { return s.conn.Close() } +// NewStreamCipher creates new stream cipher wrapper. func NewStreamCipher(conn StreamReadWriteCloser, encryptor, decryptor cipher.Stream) StreamReadWriteCloser { return &StreamCipher{ conn: conn, diff --git a/wrappers/wrap.go b/wrappers/wrap.go index 2023694..5e9581b 100644 --- a/wrappers/wrap.go +++ b/wrappers/wrap.go @@ -7,78 +7,104 @@ import ( "go.uber.org/zap" ) +// Wrap is a base interface for all wrappers in this package. type Wrap interface { Logger() *zap.SugaredLogger LocalAddr() *net.TCPAddr RemoteAddr() *net.TCPAddr } +// Writer is a base interface for writers of this package. type Writer interface { io.Writer Wrap } +// Closer is a base interface for wrappers of this package which can +// close connections. type Closer interface { io.Closer Wrap } +// WriteCloser is a base interface for wrappers of this package which +// can write to and close connections. type WriteCloser interface { io.Closer Writer } +// StreamReader is a base interface for wrappers which can read from the +// stream. type StreamReader interface { io.Reader Wrap } +// StreamReadCloser is a base interface for wrappers which can read from +// and close the connections. type StreamReadCloser interface { io.Closer StreamReader } +// StreamReadWriter is a base interface for wrappers which can read from +// and write to the connections. type StreamReadWriter interface { io.Writer StreamReader } +// StreamWriteCloser is a base interface for wrappers which can write to +// and close the connections. type StreamWriteCloser interface { io.WriteCloser Wrap } +// StreamReadWriteCloser is a base interface for stream processors. type StreamReadWriteCloser interface { io.Closer StreamReadWriter } +// PacketReader is a base interface for wrappers which reads 'packets'. +// packets are atoms so you either get a packet or you get an error You +// cannot resume reading from packet. type PacketReader interface { Read() ([]byte, error) Wrap } +// PacketWriter is a base interface for wrappers which can write packets. type PacketWriter interface { io.Writer Wrap } +// PacketReadWriter is a base interface for wrappers which can read from +// and write packets. type PacketReadWriter interface { io.Writer PacketReader } +// PacketReadCloser is a base interface for wrappers which can read +// packets and close the connection. type PacketReadCloser interface { io.Closer PacketReader } +// PacketWriteCloser is a base interface for wrappers which can write +// packets and close the connection. type PacketWriteCloser interface { io.Writer io.Closer Wrap } +// PacketReadWriteCloser is a base interface for packet processors. type PacketReadWriteCloser interface { io.Closer PacketReadWriter