func parseNameServers(nameServers []string) (
	udpAddrs, tcpAddrs []net.Addr, dohAddrs, dotAddrs []string,
) {
	for _, ns := range nameServers {
		dnsURL, err := url.Parse(ns)
		if err != nil {
			log.Printf("dns: invalid name server URI %q", ns)
			continue
		}
		switch dnsURL.Scheme {
		case "udp":
			udpAddr, err := libnet.ParseUDPAddr(dnsURL.Host, DefaultPort)
			if err != nil {
				log.Printf("dns: invalid UDP IP address %q", dnsURL.Host)
				continue
			}
			udpAddrs = append(udpAddrs, udpAddr)
		case "tcp":
			tcpAddr, err := libnet.ParseTCPAddr(dnsURL.Host, DefaultPort)
			if err != nil {
				log.Printf("dns: invalid TCP IP address %q", dnsURL.Host)
				continue
			}
			tcpAddrs = append(tcpAddrs, tcpAddr)
		case "https":
			ip := net.ParseIP(dnsURL.Hostname())
			if ip != nil {
				dotAddrs = append(dotAddrs, dnsURL.Host)
			} else {
				dohAddrs = append(dohAddrs, ns)
			}
		default:
			if len(dnsURL.Host) > 0 {
				ns = dnsURL.Host
			}
			udpAddr, err := libnet.ParseUDPAddr(ns, DefaultPort)
			if err != nil {
				log.Printf("dns: invalid UDP IP address %q", ns)
				continue
			}
			udpAddrs = append(udpAddrs, udpAddr)
		}
	}
	return udpAddrs, tcpAddrs, dohAddrs, dotAddrs
}
parseNameServers parse each name server in NameServers list based on scheme
and store the result either in udpAddrs, tcpAddrs, dohAddrs, or dotAddrs.

If the name server format contains no scheme, it will be assumed to be
"udp".
package dns
import (
	"fmt"
	"log"
	"net"
	"net/url"
	"time"
	libnet "github.com/shuLhan/share/lib/net"
)
type ServerOptions struct {
	ListenAddress string `ini:"dns:server:listen"`
	HTTPIdleTimeout time.Duration `ini:"dns:server:http.idle_timeout"`
	HTTPPort uint16 `ini:"dns:server:http.port"`
	TLSPort uint16 `ini:"dns:server:tls.port"`
	NameServers []string `ini:"dns:server:parent"`
	FallbackNS []string
	TLSCertFile string `ini:"dns:server:tls.certificate"`
	TLSPrivateKey string `ini:"dns:server:tls.private_key"`
	TLSAllowInsecure bool `ini:"dns:server:tls.allow_insecure"`
	DoHBehindProxy bool `ini:"dns:server:doh.behind_proxy"`
	PruneDelay time.Duration `ini:"dns:server:cache.prune_delay"`
	PruneThreshold time.Duration `ini:"dns:server:cache.prune_threshold"`
	ip   net.IP
	port uint16
	primaryUDP []net.Addr
	primaryTCP []net.Addr
	primaryDoh []string
	primaryDot []string
	fallbackUDP []net.Addr
	fallbackTCP []net.Addr
	fallbackDoh []string
	fallbackDot []string
}
func (opts *ServerOptions) init() (err error) {
	if len(opts.ListenAddress) == 0 {
		opts.ListenAddress = "0.0.0.0:53"
	}
	_, opts.ip, opts.port = libnet.ParseIPPort(opts.ListenAddress, DefaultPort)
	if opts.ip == nil {
		return fmt.Errorf("dns: invalid IP address '%s'", opts.ListenAddress)
	}
	if opts.HTTPIdleTimeout <= 0 {
		opts.HTTPIdleTimeout = defaultHTTPIdleTimeout
	}
	if opts.PruneDelay.Minutes() < 1 {
		opts.PruneDelay = time.Hour
	}
	if opts.PruneThreshold.Minutes() > -1 {
		opts.PruneThreshold = -1 * time.Hour
	}
	if len(opts.NameServers) == 0 {
		return nil
	}
	opts.parseNameServers()
	if len(opts.primaryUDP) == 0 && len(opts.primaryTCP) == 0 && len(opts.primaryDoh) == 0 && len(opts.primaryDot) == 0 {
		return fmt.Errorf("dns: no valid name servers")
	}
	return nil
}
func (opts *ServerOptions) getUDPAddress() *net.UDPAddr {
	return &net.UDPAddr{
		IP:   opts.ip,
		Port: int(opts.port),
	}
}
func (opts *ServerOptions) getTCPAddress() *net.TCPAddr {
	return &net.TCPAddr{
		IP:   opts.ip,
		Port: int(opts.port),
	}
}
func (opts *ServerOptions) getHTTPAddress() *net.TCPAddr {
	return &net.TCPAddr{
		IP:   opts.ip,
		Port: int(opts.HTTPPort),
	}
}
func (opts *ServerOptions) getDoTAddress() *net.TCPAddr {
	return &net.TCPAddr{
		IP:   opts.ip,
		Port: int(opts.TLSPort),
	}
}
func (opts *ServerOptions) hasFallback() bool {
	return len(opts.fallbackUDP) > 0 || len(opts.fallbackTCP) > 0 ||
		len(opts.fallbackDot) > 0 || len(opts.fallbackDoh) > 0
}