You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
2.6 KiB
103 lines
2.6 KiB
package dcs |
|
|
|
import ( |
|
"context" |
|
"io" |
|
|
|
"github.com/go-faster/errors" |
|
"nhooyr.io/websocket" |
|
|
|
"github.com/gotd/td/internal/crypto" |
|
"github.com/gotd/td/internal/mtproxy" |
|
"github.com/gotd/td/internal/mtproxy/obfuscator" |
|
"github.com/gotd/td/internal/proto/codec" |
|
"github.com/gotd/td/internal/wsutil" |
|
"github.com/gotd/td/transport" |
|
) |
|
|
|
var _ Resolver = ws{} |
|
|
|
type ws struct { |
|
dialOptions *websocket.DialOptions |
|
protocol protocol |
|
|
|
tag [4]byte |
|
rand io.Reader |
|
} |
|
|
|
func (w ws) connect(ctx context.Context, dc int, domains map[int]string) (transport.Conn, error) { |
|
addr, ok := domains[dc] |
|
if !ok { |
|
return nil, errors.Errorf("domain for %d not found", dc) |
|
} |
|
|
|
conn, resp, err := websocket.Dial(ctx, addr, w.dialOptions) |
|
if resp != nil && resp.Body != nil { |
|
_ = resp.Body.Close() |
|
} |
|
if err != nil { |
|
return nil, errors.Wrap(err, "dial ws") |
|
} |
|
obsConn := obfuscator.Obfuscated2(w.rand, wsutil.NetConn(conn)) |
|
|
|
if err := obsConn.Handshake(w.tag, dc, mtproxy.Secret{ |
|
Secret: nil, |
|
Type: mtproxy.Simple, |
|
}); err != nil { |
|
return nil, errors.Wrap(err, "handshake") |
|
} |
|
|
|
transportConn, err := w.protocol.Handshake(obsConn) |
|
if err != nil { |
|
return nil, errors.Wrap(err, "transport handshake") |
|
} |
|
|
|
return transportConn, nil |
|
} |
|
|
|
func (w ws) Primary(ctx context.Context, dc int, list List) (transport.Conn, error) { |
|
return w.connect(ctx, dc, list.Domains) |
|
} |
|
|
|
func (w ws) MediaOnly(ctx context.Context, dc int, list List) (transport.Conn, error) { |
|
return nil, errors.Errorf("can't resolve %d: MediaOnly is unsupported", dc) |
|
} |
|
|
|
func (w ws) CDN(ctx context.Context, dc int, list List) (transport.Conn, error) { |
|
return nil, errors.Errorf("can't resolve %d: CDN is unsupported", dc) |
|
} |
|
|
|
// WebsocketOptions is Websocket resolver creation options. |
|
type WebsocketOptions struct { |
|
// Dialer specifies the websocket dialer. |
|
// If Dialer is nil, then the resolver dials using websocket.DefaultDialer. |
|
DialOptions *websocket.DialOptions |
|
// Random source for MTProxy obfuscator. |
|
Rand io.Reader |
|
} |
|
|
|
func (m *WebsocketOptions) setDefaults() { |
|
if m.DialOptions == nil { |
|
m.DialOptions = &websocket.DialOptions{Subprotocols: []string{ |
|
"binary", |
|
}} |
|
} |
|
if m.Rand == nil { |
|
m.Rand = crypto.DefaultRand() |
|
} |
|
} |
|
|
|
// Websocket creates Websocket DC resolver. |
|
// |
|
// See https://core.telegram.org/mtproto/transports#websocket. |
|
func Websocket(opts WebsocketOptions) Resolver { |
|
cdc := codec.Intermediate{} |
|
opts.setDefaults() |
|
|
|
return ws{ |
|
dialOptions: opts.DialOptions, |
|
protocol: transport.NewProtocol(func() transport.Codec { return codec.NoHeader{Codec: cdc} }), |
|
tag: cdc.ObfuscatedTag(), |
|
rand: opts.Rand, |
|
} |
|
}
|
|
|