github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+incompatible/services/httpservice/client.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package httpservice
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"errors"
    10  	"net"
    11  	"net/http"
    12  	"time"
    13  )
    14  
    15  const (
    16  	connectTimeout = 3 * time.Second
    17  	requestTimeout = 30 * time.Second
    18  )
    19  
    20  var reservedIPRanges []*net.IPNet
    21  
    22  func IsReservedIP(ip net.IP) bool {
    23  	for _, ipRange := range reservedIPRanges {
    24  		if ipRange.Contains(ip) {
    25  			return true
    26  		}
    27  	}
    28  	return false
    29  }
    30  
    31  func init() {
    32  	for _, cidr := range []string{
    33  		// See https://tools.ietf.org/html/rfc6890
    34  		"0.0.0.0/8",      // This host on this network
    35  		"10.0.0.0/8",     // Private-Use
    36  		"127.0.0.0/8",    // Loopback
    37  		"169.254.0.0/16", // Link Local
    38  		"172.16.0.0/12",  // Private-Use Networks
    39  		"192.168.0.0/16", // Private-Use Networks
    40  		"::/128",         // Unspecified Address
    41  		"::1/128",        // Loopback Address
    42  		"fc00::/7",       // Unique-Local
    43  		"fe80::/10",      // Linked-Scoped Unicast
    44  	} {
    45  		_, parsed, err := net.ParseCIDR(cidr)
    46  		if err != nil {
    47  			panic(err)
    48  		}
    49  		reservedIPRanges = append(reservedIPRanges, parsed)
    50  	}
    51  }
    52  
    53  type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error)
    54  
    55  var AddressForbidden error = errors.New("address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network")
    56  
    57  func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction {
    58  	return func(ctx context.Context, network, addr string) (net.Conn, error) {
    59  		host, port, err := net.SplitHostPort(addr)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  
    64  		if allowHost != nil && allowHost(host) {
    65  			return dial(ctx, network, addr)
    66  		}
    67  
    68  		ips, err := net.LookupIP(host)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  
    73  		var firstErr error
    74  		for _, ip := range ips {
    75  			select {
    76  			case <-ctx.Done():
    77  				return nil, ctx.Err()
    78  			default:
    79  			}
    80  
    81  			if allowIP == nil || !allowIP(ip) {
    82  				continue
    83  			}
    84  
    85  			conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port))
    86  			if err == nil {
    87  				return conn, nil
    88  			}
    89  			if firstErr == nil {
    90  				firstErr = err
    91  			}
    92  		}
    93  		if firstErr == nil {
    94  			return nil, AddressForbidden
    95  		}
    96  		return nil, firstErr
    97  	}
    98  }
    99  
   100  // NewHTTPClient returns a variation the default implementation of Client.
   101  // It uses a Transport with the same settings as the default Transport
   102  // but with the following modifications:
   103  // - shorter timeout for dial and TLS handshake (defined as constant
   104  //   "connectTimeout")
   105  // - timeout for the end-to-end request (defined as constant
   106  //   "requestTimeout")
   107  func NewHTTPClient(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) *http.Client {
   108  	dialContext := (&net.Dialer{
   109  		Timeout:   connectTimeout,
   110  		KeepAlive: 30 * time.Second,
   111  	}).DialContext
   112  
   113  	if allowHost != nil || allowIP != nil {
   114  		dialContext = dialContextFilter(dialContext, allowHost, allowIP)
   115  	}
   116  
   117  	client := &http.Client{
   118  		Transport: &http.Transport{
   119  			Proxy:                 http.ProxyFromEnvironment,
   120  			DialContext:           dialContext,
   121  			MaxIdleConns:          100,
   122  			IdleConnTimeout:       90 * time.Second,
   123  			TLSHandshakeTimeout:   connectTimeout,
   124  			ExpectContinueTimeout: 1 * time.Second,
   125  			TLSClientConfig: &tls.Config{
   126  				InsecureSkipVerify: enableInsecureConnections,
   127  			},
   128  		},
   129  		Timeout: requestTimeout,
   130  	}
   131  
   132  	return client
   133  }