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 }