main
Raw Download raw file
  1// Copyright 2011 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 proxy provides support for a variety of protocols to proxy network
  6// data.
  7package proxy // import "golang.org/x/net/proxy"
  8
  9import (
 10	"errors"
 11	"net"
 12	"net/url"
 13	"os"
 14	"sync"
 15)
 16
 17// A Dialer is a means to establish a connection.
 18// Custom dialers should also implement ContextDialer.
 19type Dialer interface {
 20	// Dial connects to the given address via the proxy.
 21	Dial(network, addr string) (c net.Conn, err error)
 22}
 23
 24// Auth contains authentication parameters that specific Dialers may require.
 25type Auth struct {
 26	User, Password string
 27}
 28
 29// FromEnvironment returns the dialer specified by the proxy-related
 30// variables in the environment and makes underlying connections
 31// directly.
 32func FromEnvironment() Dialer {
 33	return FromEnvironmentUsing(Direct)
 34}
 35
 36// FromEnvironmentUsing returns the dialer specify by the proxy-related
 37// variables in the environment and makes underlying connections
 38// using the provided forwarding Dialer (for instance, a *net.Dialer
 39// with desired configuration).
 40func FromEnvironmentUsing(forward Dialer) Dialer {
 41	allProxy := allProxyEnv.Get()
 42	if len(allProxy) == 0 {
 43		return forward
 44	}
 45
 46	proxyURL, err := url.Parse(allProxy)
 47	if err != nil {
 48		return forward
 49	}
 50	proxy, err := FromURL(proxyURL, forward)
 51	if err != nil {
 52		return forward
 53	}
 54
 55	noProxy := noProxyEnv.Get()
 56	if len(noProxy) == 0 {
 57		return proxy
 58	}
 59
 60	perHost := NewPerHost(proxy, forward)
 61	perHost.AddFromString(noProxy)
 62	return perHost
 63}
 64
 65// proxySchemes is a map from URL schemes to a function that creates a Dialer
 66// from a URL with such a scheme.
 67var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
 68
 69// RegisterDialerType takes a URL scheme and a function to generate Dialers from
 70// a URL with that scheme and a forwarding Dialer. Registered schemes are used
 71// by FromURL.
 72func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
 73	if proxySchemes == nil {
 74		proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
 75	}
 76	proxySchemes[scheme] = f
 77}
 78
 79// FromURL returns a Dialer given a URL specification and an underlying
 80// Dialer for it to make network requests.
 81func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
 82	var auth *Auth
 83	if u.User != nil {
 84		auth = new(Auth)
 85		auth.User = u.User.Username()
 86		if p, ok := u.User.Password(); ok {
 87			auth.Password = p
 88		}
 89	}
 90
 91	switch u.Scheme {
 92	case "socks5", "socks5h":
 93		addr := u.Hostname()
 94		port := u.Port()
 95		if port == "" {
 96			port = "1080"
 97		}
 98		return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
 99	}
100
101	// If the scheme doesn't match any of the built-in schemes, see if it
102	// was registered by another package.
103	if proxySchemes != nil {
104		if f, ok := proxySchemes[u.Scheme]; ok {
105			return f(u, forward)
106		}
107	}
108
109	return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
110}
111
112var (
113	allProxyEnv = &envOnce{
114		names: []string{"ALL_PROXY", "all_proxy"},
115	}
116	noProxyEnv = &envOnce{
117		names: []string{"NO_PROXY", "no_proxy"},
118	}
119)
120
121// envOnce looks up an environment variable (optionally by multiple
122// names) once. It mitigates expensive lookups on some platforms
123// (e.g. Windows).
124// (Borrowed from net/http/transport.go)
125type envOnce struct {
126	names []string
127	once  sync.Once
128	val   string
129}
130
131func (e *envOnce) Get() string {
132	e.once.Do(e.init)
133	return e.val
134}
135
136func (e *envOnce) init() {
137	for _, n := range e.names {
138		e.val = os.Getenv(n)
139		if e.val != "" {
140			return
141		}
142	}
143}
144
145// reset is used by tests
146func (e *envOnce) reset() {
147	e.once = sync.Once{}
148	e.val = ""
149}