diff --git a/mtglib/internal/telegram/init.go b/mtglib/internal/telegram/init.go new file mode 100644 index 0000000..3e00353 --- /dev/null +++ b/mtglib/internal/telegram/init.go @@ -0,0 +1,53 @@ +package telegram + +import ( + "context" + "net" +) + +type preferIP uint8 + +const ( + preferIPOnlyIPv4 preferIP = iota + preferIPOnlyIPv6 + preferIPPreferIPv4 + preferIPPreferIPv6 +) + +type tgAddr struct { + network string + address string +} + +// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30 +var ( + v4Addresses = [5][]tgAddr{ + { + {network: "tcp4", address: "149.154.175.50:443"}, + }, + { + {network: "tcp4", address: "149.154.167.51:443"}, + {network: "tcp4", address: "95.161.76.100:443"}, + }, + { + {network: "tcp4", address: "149.154.175.100:443"}, + }, + { + {network: "tcp4", address: "149.154.167.91:443"}, + }, + { + {network: "tcp4", address: "149.154.171.5"}, + }, + } + v6Addresses = [5]tgAddr{ + {network: "tcp6", address: "[2001:b28:f23d:f001::a]:443"}, + {network: "tcp6", address: "[2001:67c:04e8:f002::a]:443"}, + {network: "tcp6", address: "[2001:b28:f23d:f003::a]:443"}, + {network: "tcp6", address: "[2001:67c:04e8:f004::a]:443"}, + {network: "tcp6", address: "[2001:b28:f23f:f005::a]:443"}, + } +) + +type Dialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) +} diff --git a/mtglib/internal/telegram/telegram.go b/mtglib/internal/telegram/telegram.go new file mode 100644 index 0000000..c8e3a27 --- /dev/null +++ b/mtglib/internal/telegram/telegram.go @@ -0,0 +1,75 @@ +package telegram + +import ( + "context" + "fmt" + "math/rand" + "net" + "strings" +) + +type Telegram struct { + dialer Dialer + preferIP preferIP +} + +func (t Telegram) Dial(ctx context.Context, dc int) (net.Conn, error) { + if dc < 0 || dc > 4 { + return nil, fmt.Errorf("do not know how to dial to %d", dc) + } + + var addresses []tgAddr + + if t.preferIP == preferIPOnlyIPv6 { + addresses = []tgAddr{v6Addresses[dc]} + } else { + addresses = append(addresses, v4Addresses[dc]...) + rand.Shuffle(len(addresses), func(i, j int) { + addresses[i], addresses[j] = addresses[j], addresses[i] + }) + } + + switch t.preferIP { + case preferIPPreferIPv4: + addresses = append(addresses, v6Addresses[dc]) + case preferIPPreferIPv6: + addresses = append([]tgAddr{v6Addresses[dc]}, addresses...) + case preferIPOnlyIPv4, preferIPOnlyIPv6: + } + + var ( + conn net.Conn + err error + ) + + for _, v := range addresses { + conn, err = t.dialer.DialContext(ctx, v.network, v.address) + if err == nil { + return conn, nil + } + } + + return nil, fmt.Errorf("cannot dial to %d dc: %w", dc, err) +} + +func NewTelegramDialer(dialer Dialer, ipPreference string) (*Telegram, error) { + var pref preferIP + + switch strings.ToLower(ipPreference) { + case "prefer-ipv4": + pref = preferIPPreferIPv4 + case "prefer-ipv6": + pref = preferIPPreferIPv6 + case "only-ipv4": + pref = preferIPOnlyIPv4 + case "only-ipv6": + pref = preferIPOnlyIPv6 + default: + return nil, fmt.Errorf("unknown ip preference %s", ipPreference) + } + + return &Telegram{ + dialer: dialer, + preferIP: pref, + }, nil +} diff --git a/network/network.go b/network/network.go index 9f85744..9df70a0 100644 --- a/network/network.go +++ b/network/network.go @@ -43,11 +43,9 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (ne return nil, fmt.Errorf("cannot resolve dns names: %w", err) } - if len(ips) > 1 { - rand.Shuffle(len(ips), func(i, j int) { - ips[i], ips[j] = ips[j], ips[i] - }) - } + rand.Shuffle(len(ips), func(i, j int) { + ips[i], ips[j] = ips[j], ips[i] + }) var conn net.Conn for _, v := range ips {