Add telegram api

This commit is contained in:
9seconds
2019-09-09 10:25:38 +03:00
parent 2918ed11e5
commit 3816dbf5b1
9 changed files with 203 additions and 26 deletions
+24
View File
@@ -0,0 +1,24 @@
package conntypes
import (
"crypto/rand"
"encoding/hex"
)
const ConnIDLength = 8
type ConnID [ConnIDLength]byte
func (c ConnID) String() string {
return hex.EncodeToString(c[:])
}
func NewConnID() ConnID {
var id ConnID
if _, err := rand.Read(id[:]); err != nil {
panic(err)
}
return id
}
+1
View File
@@ -9,6 +9,7 @@ require (
github.com/allegro/bigcache v1.2.1
github.com/beevik/ntp v0.2.0
github.com/cespare/xxhash v1.1.0
github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d
github.com/kr/pretty v0.1.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v1.1.0
+2
View File
@@ -40,6 +40,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d h1:hJXjZMxj0SWlMoQkzeZDLi2cmeiWKa7y1B8Rg+qaoEc=
github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+2 -1
View File
@@ -5,13 +5,14 @@ import (
"go.uber.org/zap"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/wrappers"
)
type TelegramRequest struct {
Logger *zap.SugaredLogger
ClientConn wrappers.StreamReadWriteCloser
ConnID wrappers.ConnID
ConnID conntypes.ConnID
Ctx context.Context
Cancel context.CancelFunc
ClientProtocol ClientProtocol
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"go.uber.org/zap"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
"github.com/9seconds/mtg/protocol"
"github.com/9seconds/mtg/stats"
"github.com/9seconds/mtg/telegram"
@@ -53,7 +54,7 @@ func (p *Proxy) accept(conn net.Conn) {
}
}()
connID := wrappers.NewConnID()
connID := conntypes.NewConnID()
logger := p.Logger.With("connection_id", connID)
if err := utils.InitTCP(conn); err != nil {
+106
View File
@@ -0,0 +1,106 @@
package api
import (
"bufio"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"github.com/9seconds/mtg/conntypes"
)
const (
addressesURLV4 = "https://core.telegram.org/getProxyConfig" // nolint: gas
addressesURLV6 = "https://core.telegram.org/getProxyConfigV6" // nolint: gas
)
var addressesProxyForSplitter = regexp.MustCompile(`\s+`)
func AddressesV4() (map[conntypes.DC][]string, conntypes.DC, error) {
return getAddresses(addressesURLV4)
}
func AddressesV6() (map[conntypes.DC][]string, conntypes.DC, error) {
return getAddresses(addressesURLV6)
}
func getAddresses(url string) (map[conntypes.DC][]string, conntypes.DC, error) {
resp, err := request(url)
if err != nil {
return nil, 0, fmt.Errorf("cannot get http response: %w", err)
}
defer resp.Close()
scanner := bufio.NewScanner(resp)
data := map[conntypes.DC][]string{}
var defaultDC = conntypes.DCDefaultIdx
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
switch {
case strings.HasPrefix(text, "#"):
continue
case strings.HasPrefix(text, "proxy_for"):
addr, idx, err := addressesParseProxyFor(text)
if err != nil {
return nil, 0, fmt.Errorf("cannot parse 'proxy_for' section: %w", err)
}
if addresses, ok := data[idx]; ok {
data[idx] = append(addresses, addr)
} else {
data[idx] = []string{addr}
}
case strings.HasPrefix(text, "default"):
idx, err := addressesParseDefault(text)
if err != nil {
return nil, 0, fmt.Errorf("cannot parse 'default' section: %w", err)
}
defaultDC = idx
}
}
err = scanner.Err()
if err != nil {
return nil, 0, fmt.Errorf("cannot parse http response: %w", err)
}
return data, defaultDC, nil
}
func addressesParseProxyFor(text string) (string, conntypes.DC, error) {
chunks := addressesProxyForSplitter.Split(text, 3)
if len(chunks) != 3 || chunks[0] != "proxy_for" {
return "", 0, fmt.Errorf("incorrect config %s", text)
}
dc, err := strconv.ParseInt(chunks[1], 10, 16)
if err != nil {
return "", 0, fmt.Errorf("incorrect config '%s': %w", text, err)
}
addr := strings.TrimRight(chunks[2], ";")
if _, _, err = net.SplitHostPort(addr); err != nil {
return "", 0, fmt.Errorf("incorrect config '%s': %w", text, err)
}
return addr, conntypes.DC(dc), nil
}
func addressesParseDefault(text string) (conntypes.DC, error) {
chunks := addressesProxyForSplitter.Split(text, 2)
if len(chunks) != 2 || chunks[0] != "default" {
return 0, fmt.Errorf("incorrect config '%s'", text)
}
dcString := strings.TrimRight(chunks[1], ";")
dc, err := strconv.ParseInt(dcString, 10, 16)
if err != nil {
return 0, fmt.Errorf("incorrect config '%s': %w", text, err)
}
return conntypes.DC(dc), nil
}
+38
View File
@@ -0,0 +1,38 @@
package api
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
const (
apiUserAgent = "mtg"
apiHTTPTimeout = 30 * time.Second
)
var httpClient = http.Client{
Timeout: apiHTTPTimeout,
}
func request(url string) (io.ReadCloser, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req.Header.Set("Accept", "text/plan")
req.Header.Set("User-Agent", apiUserAgent)
resp, err := httpClient.Do(req)
if err != nil {
if resp != nil {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
}
return nil, fmt.Errorf("cannot perform a request: %w", err)
}
return resp.Body, err
}
+23
View File
@@ -0,0 +1,23 @@
package api
import (
"fmt"
"io/ioutil"
)
const secretURL = "https://core.telegram.org/getProxySecret" // nolint: gas
func Secret() ([]byte, error) {
resp, err := request(secretURL)
if err != nil {
return nil, fmt.Errorf("cannot access telegram server: %w", err)
}
defer resp.Close()
secret, err := ioutil.ReadAll(resp)
if err != nil {
return nil, fmt.Errorf("cannot read response: %w", err)
}
return secret, nil
}
+5 -24
View File
@@ -2,8 +2,6 @@ package wrappers
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"net"
"time"
@@ -11,16 +9,9 @@ import (
"go.uber.org/zap"
"github.com/9seconds/mtg/config"
"github.com/9seconds/mtg/conntypes"
)
const ConnIDLength = 8
type ConnID [ConnIDLength]byte
func (c ConnID) String() string {
return hex.EncodeToString(c[:])
}
type connPurpose uint8
const (
@@ -37,7 +28,7 @@ type wrapperConn struct {
parent net.Conn
ctx context.Context
cancel context.CancelFunc
connID ConnID
connID conntypes.ConnID
logger *zap.SugaredLogger
localAddr *net.TCPAddr
remoteAddr *net.TCPAddr
@@ -121,7 +112,7 @@ func (w *wrapperConn) RemoteAddr() *net.TCPAddr {
func newConn(ctx context.Context,
cancel context.CancelFunc,
parent net.Conn,
connID ConnID,
connID conntypes.ConnID,
purpose connPurpose) StreamReadWriteCloser {
localAddr := *parent.LocalAddr().(*net.TCPAddr)
@@ -156,22 +147,12 @@ func newConn(ctx context.Context,
func NewClientConn(ctx context.Context,
cancel context.CancelFunc,
parent net.Conn,
connID ConnID) StreamReadWriteCloser {
connID conntypes.ConnID) StreamReadWriteCloser {
return newConn(ctx, cancel, parent, connID, connPurposeClient)
}
func NewTelegramConn(ctx context.Context,
cancel context.CancelFunc,
parent net.Conn) StreamReadWriteCloser {
return newConn(ctx, cancel, parent, ConnID{}, connPurposeTelegram)
}
func NewConnID() ConnID {
var id ConnID
if _, err := rand.Read(id[:]); err != nil {
panic(err)
}
return id
return newConn(ctx, cancel, parent, conntypes.ConnID{}, connPurposeTelegram)
}