github.com/ck00004/CobaltStrikeParser-Go@v1.0.14/lib/http/httpproxy/proxy.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package httpproxy provides support for HTTP proxy determination
     6  // based on environment variables, as provided by net/http's
     7  // ProxyFromEnvironment function.
     8  //
     9  // The API is not subject to the Go 1 compatibility promise and may change at
    10  // any time.
    11  package httpproxy
    12  
    13  import (
    14  	"errors"
    15  	"fmt"
    16  	"net"
    17  	"os"
    18  	"strings"
    19  	"unicode/utf8"
    20  
    21  	"github.com/ck00004/CobaltStrikeParser-Go/lib/url"
    22  
    23  	"golang.org/x/net/idna"
    24  )
    25  
    26  // Config holds configuration for HTTP proxy settings. See
    27  // FromEnvironment for details.
    28  type Config struct {
    29  	// HTTPProxy represents the value of the HTTP_PROXY or
    30  	// http_proxy environment variable. It will be used as the proxy
    31  	// URL for HTTP requests unless overridden by NoProxy.
    32  	HTTPProxy string
    33  
    34  	// HTTPSProxy represents the HTTPS_PROXY or https_proxy
    35  	// environment variable. It will be used as the proxy URL for
    36  	// HTTPS requests unless overridden by NoProxy.
    37  	HTTPSProxy string
    38  
    39  	// NoProxy represents the NO_PROXY or no_proxy environment
    40  	// variable. It specifies a string that contains comma-separated values
    41  	// specifying hosts that should be excluded from proxying. Each value is
    42  	// represented by an IP address prefix (1.2.3.4), an IP address prefix in
    43  	// CIDR notation (1.2.3.4/8), a domain name, or a special DNS label (*).
    44  	// An IP address prefix and domain name can also include a literal port
    45  	// number (1.2.3.4:80).
    46  	// A domain name matches that name and all subdomains. A domain name with
    47  	// a leading "." matches subdomains only. For example "foo.com" matches
    48  	// "foo.com" and "bar.foo.com"; ".y.com" matches "x.y.com" but not "y.com".
    49  	// A single asterisk (*) indicates that no proxying should be done.
    50  	// A best effort is made to parse the string and errors are
    51  	// ignored.
    52  	NoProxy string
    53  
    54  	// CGI holds whether the current process is running
    55  	// as a CGI handler (FromEnvironment infers this from the
    56  	// presence of a REQUEST_METHOD environment variable).
    57  	// When this is set, ProxyForURL will return an error
    58  	// when HTTPProxy applies, because a client could be
    59  	// setting HTTP_PROXY maliciously. See https://golang.org/s/cgihttpproxy.
    60  	CGI bool
    61  }
    62  
    63  // config holds the parsed configuration for HTTP proxy settings.
    64  type config struct {
    65  	// Config represents the original configuration as defined above.
    66  	Config
    67  
    68  	// httpsProxy is the parsed URL of the HTTPSProxy if defined.
    69  	httpsProxy *url.URL
    70  
    71  	// httpProxy is the parsed URL of the HTTPProxy if defined.
    72  	httpProxy *url.URL
    73  
    74  	// ipMatchers represent all values in the NoProxy that are IP address
    75  	// prefixes or an IP address in CIDR notation.
    76  	ipMatchers []matcher
    77  
    78  	// domainMatchers represent all values in the NoProxy that are a domain
    79  	// name or hostname & domain name
    80  	domainMatchers []matcher
    81  }
    82  
    83  // FromEnvironment returns a Config instance populated from the
    84  // environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the
    85  // lowercase versions thereof). HTTPS_PROXY takes precedence over
    86  // HTTP_PROXY for https requests.
    87  //
    88  // The environment values may be either a complete URL or a
    89  // "host[:port]", in which case the "http" scheme is assumed. An error
    90  // is returned if the value is a different form.
    91  func FromEnvironment() *Config {
    92  	return &Config{
    93  		HTTPProxy:  getEnvAny("HTTP_PROXY", "http_proxy"),
    94  		HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"),
    95  		NoProxy:    getEnvAny("NO_PROXY", "no_proxy"),
    96  		CGI:        os.Getenv("REQUEST_METHOD") != "",
    97  	}
    98  }
    99  
   100  func getEnvAny(names ...string) string {
   101  	for _, n := range names {
   102  		if val := os.Getenv(n); val != "" {
   103  			return val
   104  		}
   105  	}
   106  	return ""
   107  }
   108  
   109  // ProxyFunc returns a function that determines the proxy URL to use for
   110  // a given request URL. Changing the contents of cfg will not affect
   111  // proxy functions created earlier.
   112  //
   113  // A nil URL and nil error are returned if no proxy is defined in the
   114  // environment, or a proxy should not be used for the given request, as
   115  // defined by NO_PROXY.
   116  //
   117  // As a special case, if req.URL.Host is "localhost" or a loopback address
   118  // (with or without a port number), then a nil URL and nil error will be returned.
   119  func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) {
   120  	// Preprocess the Config settings for more efficient evaluation.
   121  	cfg1 := &config{
   122  		Config: *cfg,
   123  	}
   124  	cfg1.init()
   125  	return cfg1.proxyForURL
   126  }
   127  
   128  func (cfg *config) proxyForURL(reqURL *url.URL) (*url.URL, error) {
   129  	var proxy *url.URL
   130  	if reqURL.Scheme == "https" {
   131  		proxy = cfg.httpsProxy
   132  	} else if reqURL.Scheme == "http" {
   133  		proxy = cfg.httpProxy
   134  		if proxy != nil && cfg.CGI {
   135  			return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")
   136  		}
   137  	}
   138  	if proxy == nil {
   139  		return nil, nil
   140  	}
   141  	if !cfg.useProxy(canonicalAddr(reqURL)) {
   142  		return nil, nil
   143  	}
   144  
   145  	return proxy, nil
   146  }
   147  
   148  func parseProxy(proxy string) (*url.URL, error) {
   149  	if proxy == "" {
   150  		return nil, nil
   151  	}
   152  
   153  	proxyURL, err := url.Parse(proxy)
   154  	if err != nil ||
   155  		(proxyURL.Scheme != "http" &&
   156  			proxyURL.Scheme != "https" &&
   157  			proxyURL.Scheme != "socks5") {
   158  		// proxy was bogus. Try prepending "http://" to it and
   159  		// see if that parses correctly. If not, we fall
   160  		// through and complain about the original one.
   161  		if proxyURL, err := url.Parse("http://" + proxy); err == nil {
   162  			return proxyURL, nil
   163  		}
   164  	}
   165  	if err != nil {
   166  		return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
   167  	}
   168  	return proxyURL, nil
   169  }
   170  
   171  // useProxy reports whether requests to addr should use a proxy,
   172  // according to the NO_PROXY or no_proxy environment variable.
   173  // addr is always a canonicalAddr with a host and port.
   174  func (cfg *config) useProxy(addr string) bool {
   175  	if len(addr) == 0 {
   176  		return true
   177  	}
   178  	host, port, err := net.SplitHostPort(addr)
   179  	if err != nil {
   180  		return false
   181  	}
   182  	if host == "localhost" {
   183  		return false
   184  	}
   185  	ip := net.ParseIP(host)
   186  	if ip != nil {
   187  		if ip.IsLoopback() {
   188  			return false
   189  		}
   190  	}
   191  
   192  	addr = strings.ToLower(strings.TrimSpace(host))
   193  
   194  	if ip != nil {
   195  		for _, m := range cfg.ipMatchers {
   196  			if m.match(addr, port, ip) {
   197  				return false
   198  			}
   199  		}
   200  	}
   201  	for _, m := range cfg.domainMatchers {
   202  		if m.match(addr, port, ip) {
   203  			return false
   204  		}
   205  	}
   206  	return true
   207  }
   208  
   209  func (c *config) init() {
   210  	if parsed, err := parseProxy(c.HTTPProxy); err == nil {
   211  		c.httpProxy = parsed
   212  	}
   213  	if parsed, err := parseProxy(c.HTTPSProxy); err == nil {
   214  		c.httpsProxy = parsed
   215  	}
   216  
   217  	for _, p := range strings.Split(c.NoProxy, ",") {
   218  		p = strings.ToLower(strings.TrimSpace(p))
   219  		if len(p) == 0 {
   220  			continue
   221  		}
   222  
   223  		if p == "*" {
   224  			c.ipMatchers = []matcher{allMatch{}}
   225  			c.domainMatchers = []matcher{allMatch{}}
   226  			return
   227  		}
   228  
   229  		// IPv4/CIDR, IPv6/CIDR
   230  		if _, pnet, err := net.ParseCIDR(p); err == nil {
   231  			c.ipMatchers = append(c.ipMatchers, cidrMatch{cidr: pnet})
   232  			continue
   233  		}
   234  
   235  		// IPv4:port, [IPv6]:port
   236  		phost, pport, err := net.SplitHostPort(p)
   237  		if err == nil {
   238  			if len(phost) == 0 {
   239  				// There is no host part, likely the entry is malformed; ignore.
   240  				continue
   241  			}
   242  			if phost[0] == '[' && phost[len(phost)-1] == ']' {
   243  				phost = phost[1 : len(phost)-1]
   244  			}
   245  		} else {
   246  			phost = p
   247  		}
   248  		// IPv4, IPv6
   249  		if pip := net.ParseIP(phost); pip != nil {
   250  			c.ipMatchers = append(c.ipMatchers, ipMatch{ip: pip, port: pport})
   251  			continue
   252  		}
   253  
   254  		if len(phost) == 0 {
   255  			// There is no host part, likely the entry is malformed; ignore.
   256  			continue
   257  		}
   258  
   259  		// domain.com or domain.com:80
   260  		// foo.com matches bar.foo.com
   261  		// .domain.com or .domain.com:port
   262  		// *.domain.com or *.domain.com:port
   263  		if strings.HasPrefix(phost, "*.") {
   264  			phost = phost[1:]
   265  		}
   266  		matchHost := false
   267  		if phost[0] != '.' {
   268  			matchHost = true
   269  			phost = "." + phost
   270  		}
   271  		if v, err := idnaASCII(phost); err == nil {
   272  			phost = v
   273  		}
   274  		c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost})
   275  	}
   276  }
   277  
   278  var portMap = map[string]string{
   279  	"http":   "80",
   280  	"https":  "443",
   281  	"socks5": "1080",
   282  }
   283  
   284  // canonicalAddr returns url.Host but always with a ":port" suffix
   285  func canonicalAddr(url *url.URL) string {
   286  	addr := url.Hostname()
   287  	if v, err := idnaASCII(addr); err == nil {
   288  		addr = v
   289  	}
   290  	port := url.Port()
   291  	if port == "" {
   292  		port = portMap[url.Scheme]
   293  	}
   294  	return net.JoinHostPort(addr, port)
   295  }
   296  
   297  // Given a string of the form "host", "host:port", or "[ipv6::address]:port",
   298  // return true if the string includes a port.
   299  func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
   300  
   301  func idnaASCII(v string) (string, error) {
   302  	// TODO: Consider removing this check after verifying performance is okay.
   303  	// Right now punycode verification, length checks, context checks, and the
   304  	// permissible character tests are all omitted. It also prevents the ToASCII
   305  	// call from salvaging an invalid IDN, when possible. As a result it may be
   306  	// possible to have two IDNs that appear identical to the user where the
   307  	// ASCII-only version causes an error downstream whereas the non-ASCII
   308  	// version does not.
   309  	// Note that for correct ASCII IDNs ToASCII will only do considerably more
   310  	// work, but it will not cause an allocation.
   311  	if isASCII(v) {
   312  		return v, nil
   313  	}
   314  	return idna.Lookup.ToASCII(v)
   315  }
   316  
   317  func isASCII(s string) bool {
   318  	for i := 0; i < len(s); i++ {
   319  		if s[i] >= utf8.RuneSelf {
   320  			return false
   321  		}
   322  	}
   323  	return true
   324  }
   325  
   326  // matcher represents the matching rule for a given value in the NO_PROXY list
   327  type matcher interface {
   328  	// match returns true if the host and optional port or ip and optional port
   329  	// are allowed
   330  	match(host, port string, ip net.IP) bool
   331  }
   332  
   333  // allMatch matches on all possible inputs
   334  type allMatch struct{}
   335  
   336  func (a allMatch) match(host, port string, ip net.IP) bool {
   337  	return true
   338  }
   339  
   340  type cidrMatch struct {
   341  	cidr *net.IPNet
   342  }
   343  
   344  func (m cidrMatch) match(host, port string, ip net.IP) bool {
   345  	return m.cidr.Contains(ip)
   346  }
   347  
   348  type ipMatch struct {
   349  	ip   net.IP
   350  	port string
   351  }
   352  
   353  func (m ipMatch) match(host, port string, ip net.IP) bool {
   354  	if m.ip.Equal(ip) {
   355  		return m.port == "" || m.port == port
   356  	}
   357  	return false
   358  }
   359  
   360  type domainMatch struct {
   361  	host string
   362  	port string
   363  
   364  	matchHost bool
   365  }
   366  
   367  func (m domainMatch) match(host, port string, ip net.IP) bool {
   368  	if strings.HasSuffix(host, m.host) || (m.matchHost && host == m.host[1:]) {
   369  		return m.port == "" || m.port == port
   370  	}
   371  	return false
   372  }