mirror of
https://github.com/Snawoot/opera-proxy.git
synced 2025-09-02 02:30:21 +00:00
Merge pull request #57 from Snawoot/concurrent_bootstrap_dns
Concurrent bootstrap dns
This commit is contained in:
commit
0ec231b994
2 changed files with 88 additions and 77 deletions
75
main.go
75
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
|
@ -8,10 +9,12 @@ import (
|
|||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -43,6 +46,37 @@ func arg_fail(msg string) {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
type CSVArg struct {
|
||||
values []string
|
||||
}
|
||||
|
||||
func (a *CSVArg) String() string {
|
||||
if len(a.values) == 0 {
|
||||
return ""
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
wr := csv.NewWriter(buf)
|
||||
wr.Write(a.values)
|
||||
wr.Flush()
|
||||
return strings.TrimRight(buf.String(), "\n")
|
||||
}
|
||||
|
||||
func (a *CSVArg) Set(line string) error {
|
||||
rd := csv.NewReader(strings.NewReader(line))
|
||||
rd.FieldsPerRecord = -1
|
||||
rd.TrimLeadingSpace = true
|
||||
values, err := rd.Read()
|
||||
if err == io.EOF {
|
||||
a.values = nil
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse comma-separated argument: %w", err)
|
||||
}
|
||||
a.values = values
|
||||
return nil
|
||||
}
|
||||
|
||||
type CLIArgs struct {
|
||||
country string
|
||||
listCountries bool
|
||||
|
@ -55,15 +89,28 @@ type CLIArgs struct {
|
|||
apiLogin string
|
||||
apiPassword string
|
||||
apiAddress string
|
||||
bootstrapDNS string
|
||||
bootstrapDNS *CSVArg
|
||||
refresh time.Duration
|
||||
refreshRetry time.Duration
|
||||
certChainWorkaround bool
|
||||
caFile string
|
||||
}
|
||||
|
||||
func parse_args() CLIArgs {
|
||||
var args CLIArgs
|
||||
func parse_args() *CLIArgs {
|
||||
args := &CLIArgs{
|
||||
bootstrapDNS: &CSVArg{
|
||||
values: []string{
|
||||
"https://1.1.1.3/dns-query",
|
||||
"https://8.8.8.8/dns-query",
|
||||
"https://dns.google/dns-query",
|
||||
"https://security.cloudflare-dns.com/dns-query",
|
||||
"https://wikimedia-dns.org/dns-query",
|
||||
"https://dns.adguard-dns.com/dns-query",
|
||||
"https://dns.quad9.net/dns-query",
|
||||
"https://doh.cleanbrowsing.org/doh/adult-filter/",
|
||||
},
|
||||
},
|
||||
}
|
||||
flag.StringVar(&args.country, "country", "EU", "desired proxy location")
|
||||
flag.BoolVar(&args.listCountries, "list-countries", false, "list available countries and exit")
|
||||
flag.BoolVar(&args.listProxies, "list-proxies", false, "output proxy list and exit")
|
||||
|
@ -78,10 +125,10 @@ func parse_args() CLIArgs {
|
|||
flag.StringVar(&args.apiLogin, "api-login", "se0316", "SurfEasy API login")
|
||||
flag.StringVar(&args.apiPassword, "api-password", "SILrMEPBmJuhomxWkfm3JalqHX2Eheg1YhlEZiMh8II", "SurfEasy API password")
|
||||
flag.StringVar(&args.apiAddress, "api-address", "", fmt.Sprintf("override IP address of %s", API_DOMAIN))
|
||||
flag.StringVar(&args.bootstrapDNS, "bootstrap-dns", "https://1.1.1.3/dns-query",
|
||||
"DNS/DoH/DoT/DoQ resolver for initial discovering of SurfEasy API address. "+
|
||||
flag.Var(args.bootstrapDNS, "bootstrap-dns",
|
||||
"comma-separated list of DNS/DoH/DoT/DoQ resolvers for initial discovery of SurfEasy API address. "+
|
||||
"See https://github.com/ameshkov/dnslookup/ for upstream DNS URL format. "+
|
||||
"Examples: https://1.1.1.1/dns-query, quic://dns.adguard.com")
|
||||
"Examples: https://1.1.1.1/dns-query,quic://dns.adguard.com")
|
||||
flag.DurationVar(&args.refresh, "refresh", 4*time.Hour, "login refresh interval")
|
||||
flag.DurationVar(&args.refreshRetry, "refresh-retry", 5*time.Second, "login refresh retry interval")
|
||||
flag.BoolVar(&args.certChainWorkaround, "certchain-workaround", true,
|
||||
|
@ -147,26 +194,34 @@ func run() int {
|
|||
}
|
||||
|
||||
seclientDialer := dialer
|
||||
if args.apiAddress != "" || args.bootstrapDNS != "" {
|
||||
if args.apiAddress != "" || len(args.bootstrapDNS.values) > 0 {
|
||||
var apiAddress string
|
||||
if args.apiAddress != "" {
|
||||
apiAddress = args.apiAddress
|
||||
mainLogger.Info("Using fixed API host IP address = %s", apiAddress)
|
||||
} else {
|
||||
resolver, err := NewResolver(args.bootstrapDNS, args.timeout)
|
||||
resolver, err := NewResolver(args.bootstrapDNS.values, args.timeout)
|
||||
if err != nil {
|
||||
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
|
||||
return 4
|
||||
}
|
||||
|
||||
mainLogger.Info("Discovering API IP address...")
|
||||
addrs := resolver.ResolveA(API_DOMAIN)
|
||||
addrs, err := func() ([]netip.Addr, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), args.timeout)
|
||||
defer cancel()
|
||||
return resolver.LookupNetIP(ctx, "ip4", API_DOMAIN)
|
||||
}()
|
||||
if err != nil {
|
||||
mainLogger.Critical("Unable to resolve API server address: %v", err)
|
||||
return 14
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
mainLogger.Critical("Unable to resolve %s with specified bootstrap DNS", API_DOMAIN)
|
||||
return 14
|
||||
}
|
||||
|
||||
apiAddress = addrs[0]
|
||||
apiAddress = addrs[0].String()
|
||||
mainLogger.Info("Discovered address of API host = %s", apiAddress)
|
||||
}
|
||||
seclientDialer = NewFixedDialer(apiAddress, dialer)
|
||||
|
|
90
resolver.go
90
resolver.go
|
@ -1,82 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/miekg/dns"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
upstream upstream.Upstream
|
||||
resolvers upstream.ParallelResolver
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
const DOT = 0x2e
|
||||
|
||||
func NewResolver(address string, timeout time.Duration) (*Resolver, error) {
|
||||
opts := &upstream.Options{Timeout: timeout}
|
||||
u, err := upstream.AddressToUpstream(address, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func NewResolver(addresses []string, timeout time.Duration) (*Resolver, error) {
|
||||
resolvers := make([]upstream.Resolver, 0, len(addresses))
|
||||
opts := &upstream.Options{
|
||||
Timeout: timeout,
|
||||
}
|
||||
return &Resolver{upstream: u}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ResolveA(domain string) []string {
|
||||
res := make([]string, 0)
|
||||
if len(domain) == 0 {
|
||||
return res
|
||||
}
|
||||
if domain[len(domain)-1] != DOT {
|
||||
domain = domain + "."
|
||||
}
|
||||
req := dns.Msg{}
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
req.Question = []dns.Question{
|
||||
{Name: domain, Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||
}
|
||||
reply, err := r.upstream.Exchange(&req)
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
for _, rr := range reply.Answer {
|
||||
if a, ok := rr.(*dns.A); ok {
|
||||
res = append(res, a.A.String())
|
||||
for _, addr := range addresses {
|
||||
u, err := upstream.AddressToUpstream(addr, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to construct upstream resolver from string %q: %w",
|
||||
addr, err)
|
||||
}
|
||||
resolvers = append(resolvers, &upstream.UpstreamResolver{Upstream: u})
|
||||
}
|
||||
return res
|
||||
return &Resolver{
|
||||
resolvers: resolvers,
|
||||
timeout: timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ResolveAAAA(domain string) []string {
|
||||
res := make([]string, 0)
|
||||
if len(domain) == 0 {
|
||||
return res
|
||||
}
|
||||
if domain[len(domain)-1] != DOT {
|
||||
domain = domain + "."
|
||||
}
|
||||
req := dns.Msg{}
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
req.Question = []dns.Question{
|
||||
{Name: domain, Qtype: dns.TypeAAAA, Qclass: dns.ClassINET},
|
||||
}
|
||||
reply, err := r.upstream.Exchange(&req)
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
for _, rr := range reply.Answer {
|
||||
if a, ok := rr.(*dns.AAAA); ok {
|
||||
res = append(res, a.AAAA.String())
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *Resolver) Resolve(domain string) []string {
|
||||
res := r.ResolveA(domain)
|
||||
if len(res) == 0 {
|
||||
res = r.ResolveAAAA(domain)
|
||||
}
|
||||
return res
|
||||
func (r *Resolver) LookupNetIP(ctx context.Context, network string, host string) (addrs []netip.Addr, err error) {
|
||||
return r.resolvers.LookupNetIP(ctx, network, host)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue