blob: 9ff4b9a7767accf28276313880db4b47bd59db5b [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001// 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.
divyadesai19009132020-03-04 12:58:08 +000018// Custom dialers should also implement ContextDialer.
Scott Bakered4efab2020-01-13 19:12:25 -080019type 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
divyadesai19009132020-03-04 12:58:08 +000029// FromEnvironment returns the dialer specified by the proxy-related
30// variables in the environment and makes underlying connections
31// directly.
Scott Bakered4efab2020-01-13 19:12:25 -080032func FromEnvironment() Dialer {
divyadesai19009132020-03-04 12:58:08 +000033 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 {
Scott Bakered4efab2020-01-13 19:12:25 -080041 allProxy := allProxyEnv.Get()
42 if len(allProxy) == 0 {
divyadesai19009132020-03-04 12:58:08 +000043 return forward
Scott Bakered4efab2020-01-13 19:12:25 -080044 }
45
46 proxyURL, err := url.Parse(allProxy)
47 if err != nil {
divyadesai19009132020-03-04 12:58:08 +000048 return forward
Scott Bakered4efab2020-01-13 19:12:25 -080049 }
divyadesai19009132020-03-04 12:58:08 +000050 proxy, err := FromURL(proxyURL, forward)
Scott Bakered4efab2020-01-13 19:12:25 -080051 if err != nil {
divyadesai19009132020-03-04 12:58:08 +000052 return forward
Scott Bakered4efab2020-01-13 19:12:25 -080053 }
54
55 noProxy := noProxyEnv.Get()
56 if len(noProxy) == 0 {
57 return proxy
58 }
59
divyadesai19009132020-03-04 12:58:08 +000060 perHost := NewPerHost(proxy, forward)
Scott Bakered4efab2020-01-13 19:12:25 -080061 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}