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}
150