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.

104 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,
}
}