// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proxy import ( "context" "errors" "net" "net/url" "os" "strings" "sync" "socks" ) // A ContextDialer dials using a context. type ContextDialer interface { DialContext(ctx context.Context, network, address string) (net.Conn, error) } // Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment. // // The passed ctx is only used for returning the Conn, not the lifetime of the Conn. // // Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer // can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout. // // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. func Dial(ctx context.Context, network, address string) (net.Conn, error) { d := FromEnvironment() if xd, ok := d.(ContextDialer); ok { return xd.DialContext(ctx, network, address) } return dialContext(ctx, d, network, address) } // WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout // A Conn returned from a successful Dial after the context has been cancelled will be immediately closed. func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) { var ( conn net.Conn done = make(chan struct{}, 1) err error ) go func() { conn, err = d.Dial(network, address) close(done) if conn != nil && ctx.Err() != nil { conn.Close() } }() select { case <-ctx.Done(): err = ctx.Err() case <-done: } return conn, err } // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. type direct struct{} // Direct implements Dialer by making network connections directly using net.Dial or net.DialContext. var Direct = direct{} var ( _ Dialer = Direct _ ContextDialer = Direct ) // Dial directly invokes net.Dial with the supplied parameters. func (direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } // DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters. func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { var d net.Dialer return d.DialContext(ctx, network, addr) } // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // A PerHost directs connections to a default Dialer unless the host name // requested matches one of a number of exceptions. type PerHost struct { def, bypass Dialer bypassNetworks []*net.IPNet bypassIPs []net.IP bypassZones []string bypassHosts []string } // NewPerHost returns a PerHost Dialer that directs connections to either // defaultDialer or bypass, depending on whether the connection matches one of // the configured rules. func NewPerHost(defaultDialer, bypass Dialer) *PerHost { return &PerHost{ def: defaultDialer, bypass: bypass, } } // Dial connects to the address addr on the given network through either // defaultDialer or bypass. func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } return p.dialerForRequest(host).Dial(network, addr) } // DialContext connects to the address addr on the given network through either // defaultDialer or bypass. func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } d := p.dialerForRequest(host) if x, ok := d.(ContextDialer); ok { return x.DialContext(ctx, network, addr) } return dialContext(ctx, d, network, addr) } func (p *PerHost) dialerForRequest(host string) Dialer { if ip := net.ParseIP(host); ip != nil { for _, net := range p.bypassNetworks { if net.Contains(ip) { return p.bypass } } for _, bypassIP := range p.bypassIPs { if bypassIP.Equal(ip) { return p.bypass } } return p.def } for _, zone := range p.bypassZones { if strings.HasSuffix(host, zone) { return p.bypass } if host == zone[1:] { // For a zone ".example.com", we match "example.com" // too. return p.bypass } } for _, bypassHost := range p.bypassHosts { if bypassHost == host { return p.bypass } } return p.def } // AddFromString parses a string that contains comma-separated values // specifying hosts that should use the bypass proxy. Each value is either an // IP address, a CIDR range, a zone (*.example.com) or a host name // (localhost). A best effort is made to parse the string and errors are // ignored. func (p *PerHost) AddFromString(s string) { hosts := strings.Split(s, ",") for _, host := range hosts { host = strings.TrimSpace(host) if len(host) == 0 { continue } if strings.Contains(host, "/") { // We assume that it's a CIDR address like 127.0.0.0/8 if _, net, err := net.ParseCIDR(host); err == nil { p.AddNetwork(net) } continue } if ip := net.ParseIP(host); ip != nil { p.AddIP(ip) continue } if strings.HasPrefix(host, "*.") { p.AddZone(host[1:]) continue } p.AddHost(host) } } // AddIP specifies an IP address that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match an IP. func (p *PerHost) AddIP(ip net.IP) { p.bypassIPs = append(p.bypassIPs, ip) } // AddNetwork specifies an IP range that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match. func (p *PerHost) AddNetwork(net *net.IPNet) { p.bypassNetworks = append(p.bypassNetworks, net) } // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of // "example.com" matches "example.com" and all of its subdomains. func (p *PerHost) AddZone(zone string) { zone = strings.TrimSuffix(zone, ".") if !strings.HasPrefix(zone, ".") { zone = "." + zone } p.bypassZones = append(p.bypassZones, zone) } // AddHost specifies a host name that will use the bypass proxy. func (p *PerHost) AddHost(host string) { host = strings.TrimSuffix(host, ".") p.bypassHosts = append(p.bypassHosts, host) } // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package proxy provides support for a variety of protocols to proxy network // data. // package proxy // import "golang.org/x/net/proxy" // A Dialer is a means to establish a connection. // Custom dialers should also implement ContextDialer. type Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) } // Auth contains authentication parameters that specific Dialers may require. type Auth struct { User, Password string } // FromEnvironment returns the dialer specified by the proxy-related // variables in the environment and makes underlying connections // directly. func FromEnvironment() Dialer { return FromEnvironmentUsing(Direct) } // FromEnvironmentUsing returns the dialer specify by the proxy-related // variables in the environment and makes underlying connections // using the provided forwarding Dialer (for instance, a *net.Dialer // with desired configuration). func FromEnvironmentUsing(forward Dialer) Dialer { allProxy := allProxyEnv.Get() if len(allProxy) == 0 { return forward } proxyURL, err := url.Parse(allProxy) if err != nil { return forward } proxy, err := FromURL(proxyURL, forward) if err != nil { return forward } noProxy := noProxyEnv.Get() if len(noProxy) == 0 { return proxy } perHost := NewPerHost(proxy, forward) perHost.AddFromString(noProxy) return perHost } // proxySchemes is a map from URL schemes to a function that creates a Dialer // from a URL with such a scheme. var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error) // RegisterDialerType takes a URL scheme and a function to generate Dialers from // a URL with that scheme and a forwarding Dialer. Registered schemes are used // by FromURL. func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) { if proxySchemes == nil { proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error)) } proxySchemes[scheme] = f } // FromURL returns a Dialer given a URL specification and an underlying // Dialer for it to make network requests. func FromURL(u *url.URL, forward Dialer) (Dialer, error) { var auth *Auth if u.User != nil { auth = new(Auth) auth.User = u.User.Username() if p, ok := u.User.Password(); ok { auth.Password = p } } switch u.Scheme { case "socks5", "socks5h": addr := u.Hostname() port := u.Port() if port == "" { port = "1080" } return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward) } // If the scheme doesn't match any of the built-in schemes, see if it // was registered by another package. if proxySchemes != nil { if f, ok := proxySchemes[u.Scheme]; ok { return f(u, forward) } } return nil, errors.New("proxy: unknown scheme: " + u.Scheme) } var ( allProxyEnv = &envOnce{ names: []string{"ALL_PROXY", "all_proxy"}, } noProxyEnv = &envOnce{ names: []string{"NO_PROXY", "no_proxy"}, } ) // envOnce looks up an environment variable (optionally by multiple // names) once. It mitigates expensive lookups on some platforms // (e.g. Windows). // (Borrowed from net/http/transport.go) type envOnce struct { names []string once sync.Once val string } func (e *envOnce) Get() string { e.once.Do(e.init) return e.val } func (e *envOnce) init() { for _, n := range e.names { e.val = os.Getenv(n) if e.val != "" { return } } } // reset is used by tests func (e *envOnce) reset() { e.once = sync.Once{} e.val = "" } // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given // address with an optional username and password. // See RFC 1928 and RFC 1929. func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) { d := socks.NewDialer(network, address) if forward != nil { if f, ok := forward.(ContextDialer); ok { d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) { return f.DialContext(ctx, network, address) } } else { d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) { return dialContext(ctx, forward, network, address) } } } if auth != nil { up := socks.UsernamePassword{ Username: auth.User, Password: auth.Password, } d.AuthMethods = []socks.AuthMethod{ socks.AuthMethodNotRequired, socks.AuthMethodUsernamePassword, } d.Authenticate = up.Authenticate } return d, nil }