From f194ad59146555582c212cfa808c004acf548f8e Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Thu, 15 Dec 2016 14:59:49 -0500 Subject: [PATCH 1/6] define SOCKS_PROXY in the environment to use socks. e.g. SOCKS_PROXY=127.0.0.1:6969 --- Makefile | 1 + client/client_impl.go | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f40359b2f..5d4e1bce8 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ deps_frontend: deps_backend: go get -u golang.org/x/tools/cmd/cover + go get -u golang.org/x/net/proxy go get -u github.com/jteeuwen/go-bindata/... go get -u github.com/elazarl/go-bindata-assetfs/... go get -u github.com/drone/mq/... diff --git a/client/client_impl.go b/client/client_impl.go index 5f0935289..96a16a503 100644 --- a/client/client_impl.go +++ b/client/client_impl.go @@ -9,9 +9,11 @@ import ( "io/ioutil" "net/http" "net/url" + "os" "strconv" "github.com/drone/drone/model" + "golang.org/x/net/proxy" "golang.org/x/oauth2" ) @@ -76,9 +78,22 @@ func NewClientTokenTLS(uri, token string, c *tls.Config) Client { auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token}) if c != nil { if trans, ok := auther.Transport.(*oauth2.Transport); ok { - trans.Base = &http.Transport{ - TLSClientConfig: c, - Proxy: http.ProxyFromEnvironment, + if os.Getenv("SOCKS_PROXY") != "" { + dialer, err := proxy.SOCKS5("tcp", os.Getenv("SOCKS_PROXY"), nil, proxy.Direct) + if err != nil { + fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err) + os.Exit(1) + } + trans.Base = &http.Transport{ + TLSClientConfig: c, + Proxy: http.ProxyFromEnvironment, + Dial: dialer.Dial, + } + } else { + trans.Base = &http.Transport{ + TLSClientConfig: c, + Proxy: http.ProxyFromEnvironment, + } } } } From 64c6b2d7d0c5af4b0cf92ff96b946ca6208db8c6 Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Thu, 15 Dec 2016 14:59:49 -0500 Subject: [PATCH 2/6] define SOCKS_PROXY in the environment to use socks. e.g. SOCKS_PROXY=127.0.0.1:6969 --- Makefile | 1 + client/client_impl.go | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f40359b2f..5d4e1bce8 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ deps_frontend: deps_backend: go get -u golang.org/x/tools/cmd/cover + go get -u golang.org/x/net/proxy go get -u github.com/jteeuwen/go-bindata/... go get -u github.com/elazarl/go-bindata-assetfs/... go get -u github.com/drone/mq/... diff --git a/client/client_impl.go b/client/client_impl.go index 5f0935289..96a16a503 100644 --- a/client/client_impl.go +++ b/client/client_impl.go @@ -9,9 +9,11 @@ import ( "io/ioutil" "net/http" "net/url" + "os" "strconv" "github.com/drone/drone/model" + "golang.org/x/net/proxy" "golang.org/x/oauth2" ) @@ -76,9 +78,22 @@ func NewClientTokenTLS(uri, token string, c *tls.Config) Client { auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token}) if c != nil { if trans, ok := auther.Transport.(*oauth2.Transport); ok { - trans.Base = &http.Transport{ - TLSClientConfig: c, - Proxy: http.ProxyFromEnvironment, + if os.Getenv("SOCKS_PROXY") != "" { + dialer, err := proxy.SOCKS5("tcp", os.Getenv("SOCKS_PROXY"), nil, proxy.Direct) + if err != nil { + fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err) + os.Exit(1) + } + trans.Base = &http.Transport{ + TLSClientConfig: c, + Proxy: http.ProxyFromEnvironment, + Dial: dialer.Dial, + } + } else { + trans.Base = &http.Transport{ + TLSClientConfig: c, + Proxy: http.ProxyFromEnvironment, + } } } } From 0399a96ae564f9498ec099e6f0c564de30e983f1 Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Wed, 18 Jan 2017 09:34:39 -0500 Subject: [PATCH 3/6] Move golang.org/x/net/proxy dependency to vendor/ --- Makefile | 1 - vendor/vendor.json | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5d4e1bce8..f40359b2f 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,6 @@ deps_frontend: deps_backend: go get -u golang.org/x/tools/cmd/cover - go get -u golang.org/x/net/proxy go get -u github.com/jteeuwen/go-bindata/... go get -u github.com/elazarl/go-bindata-assetfs/... go get -u github.com/drone/mq/... diff --git a/vendor/vendor.json b/vendor/vendor.json index 7b2b54177..8eb902a90 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -249,6 +249,12 @@ "revision": "fb93926129b8ec0056f2f458b1f519654814edf0", "revisionTime": "2016-04-13T08:48:50+10:00" }, + { + "checksumSHA1": "LvdVRE0FqdR68SvVpRkHs1rxhcA=", + "path": "golang.org/x/net/proxy", + "revision": "468bac874d80e6ca892ecbb9bf09c72519b5b751", + "revisionTime": "2016-12-14T22:25:41Z" + }, { "path": "golang.org/x/oauth2", "revision": "8a57ed94ffd43444c0879fe75701732a38afc985", From a64696df685cf818a3c75a21d815b4962bddcb84 Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Wed, 18 Jan 2017 09:36:23 -0500 Subject: [PATCH 4/6] Move golang.org/x/net/proxy dependency to vendor/ --- vendor/golang.org/x/net/proxy/direct.go | 18 ++ vendor/golang.org/x/net/proxy/per_host.go | 140 +++++++++++++++ vendor/golang.org/x/net/proxy/proxy.go | 94 ++++++++++ vendor/golang.org/x/net/proxy/socks5.go | 210 ++++++++++++++++++++++ 4 files changed, 462 insertions(+) create mode 100644 vendor/golang.org/x/net/proxy/direct.go create mode 100644 vendor/golang.org/x/net/proxy/per_host.go create mode 100644 vendor/golang.org/x/net/proxy/proxy.go create mode 100644 vendor/golang.org/x/net/proxy/socks5.go diff --git a/vendor/golang.org/x/net/proxy/direct.go b/vendor/golang.org/x/net/proxy/direct.go new file mode 100644 index 000000000..4c5ad88b1 --- /dev/null +++ b/vendor/golang.org/x/net/proxy/direct.go @@ -0,0 +1,18 @@ +// 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 + +import ( + "net" +) + +type direct struct{} + +// Direct is a direct proxy: one that makes network connections directly. +var Direct = direct{} + +func (direct) Dial(network, addr string) (net.Conn, error) { + return net.Dial(network, addr) +} diff --git a/vendor/golang.org/x/net/proxy/per_host.go b/vendor/golang.org/x/net/proxy/per_host.go new file mode 100644 index 000000000..f540b196f --- /dev/null +++ b/vendor/golang.org/x/net/proxy/per_host.go @@ -0,0 +1,140 @@ +// 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 + +import ( + "net" + "strings" +) + +// A PerHost directs connections to a default Dialer unless the hostname +// requested matches one of a number of exceptions. +type PerHost struct { + def, bypass Dialer + + bypassNetworks []*net.IPNet + bypassIPs []net.IP + bypassZones []string + bypassHosts []string +} + +// NewPerHost returns a PerHost Dialer that directs connections to either +// defaultDialer or bypass, depending on whether the connection matches one of +// the configured rules. +func NewPerHost(defaultDialer, bypass Dialer) *PerHost { + return &PerHost{ + def: defaultDialer, + bypass: bypass, + } +} + +// Dial connects to the address addr on the given network through either +// defaultDialer or bypass. +func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + return p.dialerForRequest(host).Dial(network, addr) +} + +func (p *PerHost) dialerForRequest(host string) Dialer { + if ip := net.ParseIP(host); ip != nil { + for _, net := range p.bypassNetworks { + if net.Contains(ip) { + return p.bypass + } + } + for _, bypassIP := range p.bypassIPs { + if bypassIP.Equal(ip) { + return p.bypass + } + } + return p.def + } + + for _, zone := range p.bypassZones { + if strings.HasSuffix(host, zone) { + return p.bypass + } + if host == zone[1:] { + // For a zone "example.com", we match "example.com" + // too. + return p.bypass + } + } + for _, bypassHost := range p.bypassHosts { + if bypassHost == host { + return p.bypass + } + } + return p.def +} + +// AddFromString parses a string that contains comma-separated values +// specifying hosts that should use the bypass proxy. Each value is either an +// IP address, a CIDR range, a zone (*.example.com) or a hostname +// (localhost). A best effort is made to parse the string and errors are +// ignored. +func (p *PerHost) AddFromString(s string) { + hosts := strings.Split(s, ",") + for _, host := range hosts { + host = strings.TrimSpace(host) + if len(host) == 0 { + continue + } + if strings.Contains(host, "/") { + // We assume that it's a CIDR address like 127.0.0.0/8 + if _, net, err := net.ParseCIDR(host); err == nil { + p.AddNetwork(net) + } + continue + } + if ip := net.ParseIP(host); ip != nil { + p.AddIP(ip) + continue + } + if strings.HasPrefix(host, "*.") { + p.AddZone(host[1:]) + continue + } + p.AddHost(host) + } +} + +// AddIP specifies an IP address that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match an IP. +func (p *PerHost) AddIP(ip net.IP) { + p.bypassIPs = append(p.bypassIPs, ip) +} + +// AddNetwork specifies an IP range that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match. +func (p *PerHost) AddNetwork(net *net.IPNet) { + p.bypassNetworks = append(p.bypassNetworks, net) +} + +// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of +// "example.com" matches "example.com" and all of its subdomains. +func (p *PerHost) AddZone(zone string) { + if strings.HasSuffix(zone, ".") { + zone = zone[:len(zone)-1] + } + if !strings.HasPrefix(zone, ".") { + zone = "." + zone + } + p.bypassZones = append(p.bypassZones, zone) +} + +// AddHost specifies a hostname that will use the bypass proxy. +func (p *PerHost) AddHost(host string) { + if strings.HasSuffix(host, ".") { + host = host[:len(host)-1] + } + p.bypassHosts = append(p.bypassHosts, host) +} diff --git a/vendor/golang.org/x/net/proxy/proxy.go b/vendor/golang.org/x/net/proxy/proxy.go new file mode 100644 index 000000000..78a8b7bee --- /dev/null +++ b/vendor/golang.org/x/net/proxy/proxy.go @@ -0,0 +1,94 @@ +// 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" +) + +// 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 := os.Getenv("all_proxy") + 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 := os.Getenv("no_proxy") + 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": + return SOCKS5("tcp", u.Host, 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) +} diff --git a/vendor/golang.org/x/net/proxy/socks5.go b/vendor/golang.org/x/net/proxy/socks5.go new file mode 100644 index 000000000..9b9628239 --- /dev/null +++ b/vendor/golang.org/x/net/proxy/socks5.go @@ -0,0 +1,210 @@ +// 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 + +import ( + "errors" + "io" + "net" + "strconv" +) + +// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address +// with an optional username and password. See RFC 1928. +func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) { + s := &socks5{ + network: network, + addr: addr, + forward: forward, + } + if auth != nil { + s.user = auth.User + s.password = auth.Password + } + + return s, nil +} + +type socks5 struct { + user, password string + network, addr string + forward Dialer +} + +const socks5Version = 5 + +const ( + socks5AuthNone = 0 + socks5AuthPassword = 2 +) + +const socks5Connect = 1 + +const ( + socks5IP4 = 1 + socks5Domain = 3 + socks5IP6 = 4 +) + +var socks5Errors = []string{ + "", + "general failure", + "connection forbidden", + "network unreachable", + "host unreachable", + "connection refused", + "TTL expired", + "command not supported", + "address type not supported", +} + +// Dial connects to the address addr on the network net via the SOCKS5 proxy. +func (s *socks5) Dial(network, addr string) (net.Conn, error) { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) + } + + conn, err := s.forward.Dial(s.network, s.addr) + if err != nil { + return nil, err + } + closeConn := &conn + defer func() { + if closeConn != nil { + (*closeConn).Close() + } + }() + + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return nil, errors.New("proxy: failed to parse port number: " + portStr) + } + if port < 1 || port > 0xffff { + return nil, errors.New("proxy: port number out of range: " + portStr) + } + + // the size here is just an estimate + buf := make([]byte, 0, 6+len(host)) + + buf = append(buf, socks5Version) + if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { + buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) + } else { + buf = append(buf, 1 /* num auth methods */, socks5AuthNone) + } + + if _, err := conn.Write(buf); err != nil { + return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + if buf[0] != 5 { + return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + if buf[1] == socks5AuthPassword { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + + if _, err := conn.Write(buf); err != nil { + return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if buf[1] != 0 { + return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + + buf = buf[:0] + buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) + + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, socks5IP4) + ip = ip4 + } else { + buf = append(buf, socks5IP6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return nil, errors.New("proxy: destination hostname too long: " + host) + } + buf = append(buf, socks5Domain) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + + if _, err := conn.Write(buf); err != nil { + return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:4]); err != nil { + return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + failure := "unknown error" + if int(buf[1]) < len(socks5Errors) { + failure = socks5Errors[buf[1]] + } + + if len(failure) > 0 { + return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case socks5IP4: + bytesToDiscard = net.IPv4len + case socks5IP6: + bytesToDiscard = net.IPv6len + case socks5Domain: + _, err := io.ReadFull(conn, buf[:1]) + if err != nil { + return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + // Also need to discard the port number + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + closeConn = nil + return conn, nil +} From eb7e79e887f2d0377f1ea29cc5be5c60552fae75 Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Wed, 18 Jan 2017 10:26:46 -0500 Subject: [PATCH 5/6] Propagate errors from NewClientTokenTLS back to caller. --- client/client_impl.go | 6 +++--- drone/util.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/client_impl.go b/client/client_impl.go index 96a16a503..1aea4a3d8 100644 --- a/client/client_impl.go +++ b/client/client_impl.go @@ -73,7 +73,7 @@ func NewClientToken(uri, token string) Client { // NewClientTokenTLS returns a client at the specified url that authenticates // all outbound requests with the given token and tls.Config if provided. -func NewClientTokenTLS(uri, token string, c *tls.Config) Client { +func NewClientTokenTLS(uri, token string, c *tls.Config) (Client, error) { config := new(oauth2.Config) auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token}) if c != nil { @@ -82,7 +82,7 @@ func NewClientTokenTLS(uri, token string, c *tls.Config) Client { dialer, err := proxy.SOCKS5("tcp", os.Getenv("SOCKS_PROXY"), nil, proxy.Direct) if err != nil { fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err) - os.Exit(1) + return nil, err } trans.Base = &http.Transport{ TLSClientConfig: c, @@ -97,7 +97,7 @@ func NewClientTokenTLS(uri, token string, c *tls.Config) Client { } } } - return &client{client: auther, base: uri, token: token} + return &client{client: auther, base: uri, token: token}, nil } // Self returns the currently authenticated user. diff --git a/drone/util.go b/drone/util.go index e90d39b53..da3be2550 100644 --- a/drone/util.go +++ b/drone/util.go @@ -31,7 +31,7 @@ func newClient(c *cli.Context) (client.Client, error) { tlsConfig := &tls.Config{RootCAs: certs} // create the drone client with TLS options - return client.NewClientTokenTLS(server, token, tlsConfig), nil + return client.NewClientTokenTLS(server, token, tlsConfig) } func parseRepo(str string) (user, repo string, err error) { From 244490b4d4ec48b8a3a1f1ef8af42fc903990009 Mon Sep 17 00:00:00 2001 From: "Veg (Martin Wellard)" Date: Wed, 18 Jan 2017 11:28:48 -0500 Subject: [PATCH 6/6] Remove logspam when SOCKS proxy fails to connect. --- client/client_impl.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/client_impl.go b/client/client_impl.go index 1aea4a3d8..182bc7e0d 100644 --- a/client/client_impl.go +++ b/client/client_impl.go @@ -81,7 +81,6 @@ func NewClientTokenTLS(uri, token string, c *tls.Config) (Client, error) { if os.Getenv("SOCKS_PROXY") != "" { dialer, err := proxy.SOCKS5("tcp", os.Getenv("SOCKS_PROXY"), nil, proxy.Direct) if err != nil { - fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err) return nil, err } trans.Base = &http.Transport{