mirror of
https://github.com/golang/net.git
synced 2026-04-01 02:47:08 +09:00
Environment variable 'ALL_PROXY=socks5h://example.com' is commonly used to specify a SOCKS5 proxy server. In curl, 'socks5' means the host name will be resolved locally, and 'socks5h' means the host name will be resolved by the server. Go SOCKS5 client always uses the server to resolve host names. So this change just added socks5h as a supported URL scheme. Fixes golang/go#13454 Change-Id: I06d2b07f66cd0923c114dba4df0f884b39e58bc0 Reviewed-on: https://go-review.googlesource.com/c/156517 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
// 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"
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// A Dialer is a means to establish a connection.
|
|
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.
|
|
func FromEnvironment() Dialer {
|
|
allProxy := allProxyEnv.Get()
|
|
if len(allProxy) == 0 {
|
|
return Direct
|
|
}
|
|
|
|
proxyURL, err := url.Parse(allProxy)
|
|
if err != nil {
|
|
return Direct
|
|
}
|
|
proxy, err := FromURL(proxyURL, Direct)
|
|
if err != nil {
|
|
return Direct
|
|
}
|
|
|
|
noProxy := noProxyEnv.Get()
|
|
if len(noProxy) == 0 {
|
|
return proxy
|
|
}
|
|
|
|
perHost := NewPerHost(proxy, Direct)
|
|
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 = ""
|
|
}
|