2020-09-25 14:39:11 +00:00
package oracle
import (
2022-03-04 11:09:43 +00:00
"fmt"
2020-09-25 14:39:11 +00:00
"net"
2022-03-04 11:09:43 +00:00
"net/http"
"syscall"
"github.com/nspcc-dev/neo-go/pkg/config"
2020-09-25 14:39:11 +00:00
)
// reservedCIDRs is a list of ip addresses for private networks.
// https://tools.ietf.org/html/rfc6890
var reservedCIDRs = [ ] string {
// IPv4
"10.0.0.0/8" ,
"100.64.0.0/10" ,
"172.16.0.0/12" ,
"192.0.0.0/24" ,
"192.168.0.0/16" ,
"198.18.0.0/15" ,
// IPv6
"fc00::/7" ,
}
var privateNets = make ( [ ] net . IPNet , 0 , len ( reservedCIDRs ) )
func init ( ) {
for i := range reservedCIDRs {
_ , ipNet , err := net . ParseCIDR ( reservedCIDRs [ i ] )
if err != nil {
panic ( err )
}
privateNets = append ( privateNets , * ipNet )
}
}
func isReserved ( ip net . IP ) bool {
if ! ip . IsGlobalUnicast ( ) {
return true
}
for i := range privateNets {
if privateNets [ i ] . Contains ( ip ) {
return true
}
}
return false
}
2022-03-04 11:09:43 +00:00
func getDefaultClient ( cfg config . OracleConfiguration ) * http . Client {
d := & net . Dialer { }
if ! cfg . AllowPrivateHost {
// Control is used after request URI is resolved and network connection (network
2022-04-20 18:30:09 +00:00
// file descriptor) is created, but right before listening/dialing
2022-03-04 11:09:43 +00:00
// is started.
2022-04-20 18:30:09 +00:00
// `address` represents a resolved IP address in the format of ip:port. `address`
2022-03-04 11:09:43 +00:00
// is presented in its final (resolved) form that was used directly for network
// connection establishing.
// Control is called for each item in the set of IP addresses got from request
// URI resolving. The first network connection with address that passes Control
// function will be used for further request processing. Network connection
// with address that failed Control will be ignored. If all the connections
2022-04-20 18:30:09 +00:00
// fail Control, the most relevant error (the one from the first address)
2022-03-04 11:09:43 +00:00
// will be returned after `Client.Do`.
d . Control = func ( network , address string , c syscall . RawConn ) error {
host , _ , err := net . SplitHostPort ( address )
if err != nil {
2023-03-15 12:47:38 +00:00
return fmt . Errorf ( "%w: failed to split address %s: %s" , ErrRestrictedRedirect , address , err ) //nolint:errorlint // errorlint: non-wrapping format verb for fmt.Errorf. Use `%w` to format errors
2022-03-04 11:09:43 +00:00
}
ip := net . ParseIP ( host )
if ip == nil {
return fmt . Errorf ( "%w: failed to parse IP address %s" , ErrRestrictedRedirect , address )
}
if isReserved ( ip ) {
return fmt . Errorf ( "%w: IP is not global unicast" , ErrRestrictedRedirect )
}
return nil
}
}
var client http . Client
client . Transport = & http . Transport {
DisableKeepAlives : true ,
// Do not set DialTLSContext, so that DialContext will be used to establish the
2022-04-20 18:30:09 +00:00
// connection. After that, TLS connection will be added to a persistent connection
2022-03-04 11:09:43 +00:00
// by standard library code and handshaking will be performed.
DialContext : d . DialContext ,
}
client . Timeout = cfg . RequestTimeout
client . CheckRedirect = func ( req * http . Request , via [ ] * http . Request ) error {
2022-05-03 20:18:53 +00:00
if len ( via ) > maxRedirections { // from https://github.com/neo-project/neo-modules/pull/698
2022-03-04 11:09:43 +00:00
return fmt . Errorf ( "%w: %d redirections are reached" , ErrRestrictedRedirect , maxRedirections )
}
2022-05-11 04:23:29 +00:00
if len ( via ) > 0 && via [ 0 ] . URL . Scheme == "https" && req . URL . Scheme != "https" {
lastHop := via [ len ( via ) - 1 ] . URL
return fmt . Errorf ( "%w: redirected from secure URL %s to insecure URL %s" , ErrRestrictedRedirect , lastHop , req . URL )
}
2022-03-04 11:09:43 +00:00
return nil
}
return & client
}