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.
|
---|
7 | package proxy // import "golang.org/x/net/proxy"
|
---|
8 |
|
---|
9 | import (
|
---|
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.
|
---|
19 | type 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.
|
---|
25 | type 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.
|
---|
32 | func 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).
|
---|
40 | func 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.
|
---|
67 | var 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.
|
---|
72 | func 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.
|
---|
81 | func 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 |
|
---|
112 | var (
|
---|
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)
|
---|
125 | type envOnce struct {
|
---|
126 | names []string
|
---|
127 | once sync.Once
|
---|
128 | val string
|
---|
129 | }
|
---|
130 |
|
---|
131 | func (e *envOnce) Get() string {
|
---|
132 | e.once.Do(e.init)
|
---|
133 | return e.val
|
---|
134 | }
|
---|
135 |
|
---|
136 | func (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
|
---|
146 | func (e *envOnce) reset() {
|
---|
147 | e.once = sync.Once{}
|
---|
148 | e.val = ""
|
---|
149 | }
|
---|