github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/oracle/network.go (about)

     1  package oracle
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	"syscall"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/config"
    10  )
    11  
    12  // reservedCIDRs is a list of ip addresses for private networks.
    13  // https://tools.ietf.org/html/rfc6890
    14  var reservedCIDRs = []string{
    15  	// IPv4
    16  	"10.0.0.0/8",
    17  	"100.64.0.0/10",
    18  	"172.16.0.0/12",
    19  	"192.0.0.0/24",
    20  	"192.168.0.0/16",
    21  	"198.18.0.0/15",
    22  	// IPv6
    23  	"fc00::/7",
    24  }
    25  
    26  var privateNets = make([]net.IPNet, 0, len(reservedCIDRs))
    27  
    28  func init() {
    29  	for i := range reservedCIDRs {
    30  		_, ipNet, err := net.ParseCIDR(reservedCIDRs[i])
    31  		if err != nil {
    32  			panic(err)
    33  		}
    34  		privateNets = append(privateNets, *ipNet)
    35  	}
    36  }
    37  
    38  func isReserved(ip net.IP) bool {
    39  	if !ip.IsGlobalUnicast() {
    40  		return true
    41  	}
    42  	for i := range privateNets {
    43  		if privateNets[i].Contains(ip) {
    44  			return true
    45  		}
    46  	}
    47  	return false
    48  }
    49  
    50  func getDefaultClient(cfg config.OracleConfiguration) *http.Client {
    51  	d := &net.Dialer{}
    52  	if !cfg.AllowPrivateHost {
    53  		// Control is used after request URI is resolved and network connection (network
    54  		// file descriptor) is created, but right before listening/dialing
    55  		// is started.
    56  		// `address` represents a resolved IP address in the format of ip:port. `address`
    57  		// is presented in its final (resolved) form that was used directly for network
    58  		// connection establishing.
    59  		// Control is called for each item in the set of IP addresses got from request
    60  		// URI resolving. The first network connection with address that passes Control
    61  		// function will be used for further request processing. Network connection
    62  		// with address that failed Control will be ignored. If all the connections
    63  		// fail Control, the most relevant error (the one from the first address)
    64  		// will be returned after `Client.Do`.
    65  		d.Control = func(network, address string, c syscall.RawConn) error {
    66  			host, _, err := net.SplitHostPort(address)
    67  			if err != nil {
    68  				return fmt.Errorf("%w: failed to split address %s: %w", ErrRestrictedRedirect, address, err)
    69  			}
    70  			ip := net.ParseIP(host)
    71  			if ip == nil {
    72  				return fmt.Errorf("%w: failed to parse IP address %s", ErrRestrictedRedirect, address)
    73  			}
    74  			if isReserved(ip) {
    75  				return fmt.Errorf("%w: IP is not global unicast", ErrRestrictedRedirect)
    76  			}
    77  			return nil
    78  		}
    79  	}
    80  	var client http.Client
    81  	client.Transport = &http.Transport{
    82  		DisableKeepAlives: true,
    83  		// Do not set DialTLSContext, so that DialContext will be used to establish the
    84  		// connection. After that, TLS connection will be added to a persistent connection
    85  		// by standard library code and handshaking will be performed.
    86  		DialContext: d.DialContext,
    87  	}
    88  	client.Timeout = cfg.RequestTimeout
    89  	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
    90  		if len(via) > maxRedirections { // from https://github.com/neo-project/neo-modules/pull/698
    91  			return fmt.Errorf("%w: %d redirections are reached", ErrRestrictedRedirect, maxRedirections)
    92  		}
    93  		if len(via) > 0 && via[0].URL.Scheme == "https" && req.URL.Scheme != "https" {
    94  			lastHop := via[len(via)-1].URL
    95  			return fmt.Errorf("%w: redirected from secure URL %s to insecure URL %s", ErrRestrictedRedirect, lastHop, req.URL)
    96  		}
    97  		return nil
    98  	}
    99  	return &client
   100  }