github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/connection/dns_option.go (about)

     1  /*
     2   * Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package connection
    19  
    20  import (
    21  	"encoding/json"
    22  	"net"
    23  	"strings"
    24  
    25  	"github.com/mysteriumnetwork/node/utils/stringutil"
    26  	"github.com/pkg/errors"
    27  	"github.com/rs/zerolog/log"
    28  )
    29  
    30  // DNSOption defines DNS server selection strategy for consumer
    31  type DNSOption string
    32  
    33  const (
    34  	// DNSOptionAuto (default) tries the following with fallbacks: provider's DNS -> client's system DNS -> public DNS
    35  	DNSOptionAuto = DNSOption("auto")
    36  	// DNSOptionProvider uses DNS servers from provider's system configuration
    37  	DNSOptionProvider = DNSOption("provider")
    38  	// DNSOptionSystem uses DNS servers from client's system configuration
    39  	DNSOptionSystem = DNSOption("system")
    40  )
    41  
    42  // NewDNSOption creates and validates DNSOption
    43  func NewDNSOption(str string) (DNSOption, error) {
    44  	opt := DNSOption(str)
    45  	switch opt {
    46  	case DNSOptionAuto, DNSOptionProvider, DNSOptionSystem, "":
    47  		return opt, nil
    48  	}
    49  	// It may also be a set of IP addresses, e.g. 1.1.1.1,8.8.8.8
    50  	split := strings.Split(str, ",")
    51  	for _, s := range split {
    52  		if ip := net.ParseIP(s); ip == nil {
    53  			return "", errors.New("invalid IP address provided as a DNS option: " + s)
    54  		}
    55  	}
    56  	return opt, nil
    57  }
    58  
    59  // UnmarshalJSON parses JSON → DNSOption
    60  func (o *DNSOption) UnmarshalJSON(data []byte) error {
    61  	var str string
    62  	if err := json.Unmarshal(data, &str); err != nil {
    63  		return err
    64  	}
    65  	option, err := NewDNSOption(str)
    66  	if err != nil {
    67  		return err
    68  	}
    69  	*o = option
    70  	return nil
    71  }
    72  
    73  // Exact returns a slice of DNS server IPs, if they were set
    74  func (o DNSOption) Exact() (servers []string, ok bool) {
    75  	switch o {
    76  	case DNSOptionAuto, DNSOptionProvider, DNSOptionSystem:
    77  		return nil, false
    78  	}
    79  	return stringutil.Split(string(o), ','), true
    80  }
    81  
    82  // ResolveIPs resolves DNS server IPs on the consumer side using self as the
    83  // consumer preference and `providerDNS` argument as received from the provider
    84  func (o *DNSOption) ResolveIPs(providerDNS string) ([]string, error) {
    85  	log.Debug().Msg("Selecting DNS servers using strategy: " + string(*o))
    86  	if exact, ok := o.Exact(); ok {
    87  		return exact, nil
    88  	}
    89  	switch *o {
    90  	case DNSOptionProvider:
    91  		return selectProviderDNS(providerDNS)
    92  	case DNSOptionSystem:
    93  		return nil, nil
    94  	case DNSOptionAuto:
    95  		log.Debug().Msg("Attempting to use provider DNS")
    96  		if providerDNS, err := selectProviderDNS(providerDNS); err == nil {
    97  			return providerDNS, nil
    98  		}
    99  		log.Debug().Msg("Attempting to use system DNS")
   100  		return nil, nil
   101  	}
   102  	log.Debug().Msg("Falling back to public DNS")
   103  	return []string{"1.1.1.1", "8.8.8.8"}, nil
   104  }
   105  
   106  func selectProviderDNS(providerDNS string) ([]string, error) {
   107  	opt, err := NewDNSOption(providerDNS)
   108  	if err != nil {
   109  		return nil, errors.Wrap(err, "can't parse provider DNS string")
   110  	}
   111  	servers, ok := opt.Exact()
   112  	if !ok || len(servers) == 0 {
   113  		return nil, errors.New("provider DNS is not available")
   114  	}
   115  	return servers, nil
   116  }