package dcs

import (
	
	
	
	
	
	
	
	
	
	

	

	
	
	
)

var dnsKey struct {
	rsa.PublicKey
	once sync.Once
	eBig *big.Int
}

//nolint:gochecknoinits
func () {
	dnsKey.once.Do(func() {
		,  := crypto.ParseRSAPublicKeys([]byte(`-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAyr+18Rex2ohtVy8sroGPBwXD3DOoKCSpjDqYoXgCqB7ioln4eDCF
fOBUlfXUEvM/fnKCpF46VkAftlb4VuPDeQSS/ZxZYEGqHaywlroVnXHIjgqoxiAd
192xRGreuXIaUKmkwlM9JID9WS2jUsTpzQ91L8MEPLJ/4zrBwZua8W5fECwCCh2c
9G5IzzBm+otMS/YKwmR1olzRCyEkyAEjXWqBI9Ftv5eG8m0VkBzOG655WIYdyV0H
fDK/NWcvGqa0w/nriMD6mDjKOryamw0OP9QuYgMN0C9xMW9y8SmP4h92OAWodTYg
Y1hZCxdv6cs5UnW9+PWvS+WIbkh+GaWYxwIDAQAB
-----END RSA PUBLIC KEY-----`))
		if  != nil {
			panic()
		}

		dnsKey.PublicKey = *[0]
		dnsKey.eBig = big.NewInt(int64(dnsKey.E))
	})
}

// parseDNSList decodes raw encrypted simple config.
//
// Notice that parseDNSList does not decode base64, user should do it manually.
func ( [256]byte) (tg.HelpConfigSimple, error) {
	// See https://github.com/tdlib/td/blob/master/td/telegram/ConfigManager.cpp#L148.
	 := new(big.Int).SetBytes([:])
	 := new(big.Int).Exp(, dnsKey.eBig, dnsKey.N)

	 := make([]byte, 256)
	if !crypto.FillBytes(, ) {
		return tg.HelpConfigSimple{}, errors.New("dataRSA has invalid size")
	}

	,  := aes.NewCipher([:32])
	if  != nil {
		return tg.HelpConfigSimple{}, 
	}
	 := cipher.NewCBCDecrypter(, [16:32])
	 := [32:]
	.CryptBlocks(, )

	 := [:len()-16]
	 := sha256.Sum256()
	 := [len()-16:]

	if !bytes.Equal([:16], ) {
		return tg.HelpConfigSimple{}, errors.New("hash mismatch")
	}

	var  tg.HelpConfigSimple
	if  := .Decode(&bin.Buffer{Buf: [4:]});  != nil {
		return tg.HelpConfigSimple{}, 
	}
	return , nil
}

type sortByLen []string

func ( sortByLen) () int {
	return len()
}

func ( sortByLen) (,  int) bool {
	return len([]) > len([])
}

func ( sortByLen) (,  int) {
	[], [] = [], []
}

// DNSConfig is DC connection config obtained from DNS.
type DNSConfig struct {
	// Date field of HelpConfigSimple.
	Date int
	// Expires field of HelpConfigSimple.
	Expires int
	// Rules field of HelpConfigSimple.
	Rules []tg.AccessPointRule
}

// Options returns DC options from this config.
func ( DNSConfig) () ( []tg.DCOption) {
	 := func( int) string {
		return net.IPv4(
			byte(),
			byte(>>8),
			byte(>>16),
			byte(>>24),
		).String()
	}
	for ,  := range .Rules {
		for ,  := range .IPs {
			switch ip := .(type) {
			case *tg.IPPort:
				 = append(, tg.DCOption{
					ID:        .DCID,
					IPAddress: (.Ipv4),
					Port:      .Port,
				})
			case *tg.IPPortSecret:
				 = append(, tg.DCOption{
					TCPObfuscatedOnly: true,
					ID:                .DCID,
					IPAddress:         (.Ipv4),
					Port:              .Port,
					Secret:            .Secret,
				})
			}
		}
	}
	return 
}

// ParseDNSConfig parses tg.HelpConfigSimple from TXT response.
func ( []string) (DNSConfig, error) {
	 := base64.StdEncoding
	const (
		 = 256
		 = 344
	)
	sort.Sort(sortByLen())

	var  int
	for  := range  {
		 += len([])
	}
	if  !=  {
		return DNSConfig{}, errors.Errorf("invalid input length %d", )
	}

	var (
		 []byte
		 []byte
	)
	 := 0
	for  := range  {
		 += copy([:], [])
	}

	if ,  := .Decode([:], [:]);  != nil {
		return DNSConfig{}, errors.Wrap(, "decode")
	}

	,  := parseDNSList()
	if  != nil {
		return DNSConfig{}, errors.Wrap(, "decrypt config")
	}

	return DNSConfig{
		Date:    .Date,
		Expires: .Expires,
		Rules:   .Rules,
	}, nil
}