Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
140
plugins/common/proxy/connect.go
Normal file
140
plugins/common/proxy/connect.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
// httpConnectProxy proxies (only?) TCP over a HTTP tunnel using the CONNECT method
|
||||
type httpConnectProxy struct {
|
||||
forward proxy.Dialer
|
||||
url *url.URL
|
||||
}
|
||||
|
||||
func (c *httpConnectProxy) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// Prevent using UDP
|
||||
if network == "udp" {
|
||||
return nil, fmt.Errorf("cannot proxy %q traffic over HTTP CONNECT", network)
|
||||
}
|
||||
|
||||
var proxyConn net.Conn
|
||||
var err error
|
||||
if dialer, ok := c.forward.(proxy.ContextDialer); ok {
|
||||
proxyConn, err = dialer.DialContext(ctx, "tcp", c.url.Host)
|
||||
} else {
|
||||
shim := contextDialerShim{c.forward}
|
||||
proxyConn, err = shim.DialContext(ctx, "tcp", c.url.Host)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add and strip http:// to extract authority portion of the URL
|
||||
// since CONNECT doesn't use a full URL. The request header would
|
||||
// look something like: "CONNECT www.influxdata.com:443 HTTP/1.1"
|
||||
requestURL, err := url.Parse("http://" + addr)
|
||||
if err != nil {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
requestURL.Scheme = ""
|
||||
|
||||
// Build HTTP CONNECT request
|
||||
req, err := http.NewRequest(http.MethodConnect, requestURL.String(), nil)
|
||||
if err != nil {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
req.Close = false
|
||||
if password, hasAuth := c.url.User.Password(); hasAuth {
|
||||
req.SetBasicAuth(c.url.User.Username(), password)
|
||||
}
|
||||
|
||||
err = req.Write(proxyConn)
|
||||
if err != nil {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(proxyConn), req)
|
||||
if err != nil {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("failed to connect to proxy: %q", resp.Status)
|
||||
}
|
||||
|
||||
return proxyConn, nil
|
||||
}
|
||||
|
||||
func (c *httpConnectProxy) Dial(network, addr string) (net.Conn, error) {
|
||||
return c.DialContext(context.Background(), network, addr)
|
||||
}
|
||||
|
||||
func newHTTPConnectProxy(proxyURL *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
|
||||
return &httpConnectProxy{forward, proxyURL}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register new proxy types
|
||||
proxy.RegisterDialerType("http", newHTTPConnectProxy)
|
||||
proxy.RegisterDialerType("https", newHTTPConnectProxy)
|
||||
}
|
||||
|
||||
// contextDialerShim allows cancellation of the dial from a context even if the underlying
|
||||
// dialer does not implement `proxy.ContextDialer`. Arguably, this shouldn't actually get run,
|
||||
// unless a new proxy type is added that doesn't implement `proxy.ContextDialer`, as all the
|
||||
// standard library dialers implement `proxy.ContextDialer`.
|
||||
type contextDialerShim struct {
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
func (cd *contextDialerShim) Dial(network, addr string) (net.Conn, error) {
|
||||
return cd.dialer.Dial(network, addr)
|
||||
}
|
||||
|
||||
func (cd *contextDialerShim) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
var (
|
||||
conn net.Conn
|
||||
done = make(chan struct{}, 1)
|
||||
err error
|
||||
)
|
||||
|
||||
go func() {
|
||||
conn, err = cd.dialer.Dial(network, addr)
|
||||
close(done)
|
||||
if conn != nil && ctx.Err() != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
37
plugins/common/proxy/dialer.go
Normal file
37
plugins/common/proxy/dialer.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type ProxiedDialer struct {
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
func (pd *ProxiedDialer) Dial(network, addr string) (net.Conn, error) {
|
||||
return pd.dialer.Dial(network, addr)
|
||||
}
|
||||
|
||||
func (pd *ProxiedDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if contextDialer, ok := pd.dialer.(proxy.ContextDialer); ok {
|
||||
return contextDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
contextDialer := contextDialerShim{pd.dialer}
|
||||
return contextDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
func (pd *ProxiedDialer) DialTimeout(network, addr string, timeout time.Duration) (net.Conn, error) {
|
||||
ctx := context.Background()
|
||||
if timeout.Seconds() != 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
return pd.DialContext(ctx, network, addr)
|
||||
}
|
57
plugins/common/proxy/proxy.go
Normal file
57
plugins/common/proxy/proxy.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type HTTPProxy struct {
|
||||
UseSystemProxy bool `toml:"use_system_proxy"`
|
||||
HTTPProxyURL string `toml:"http_proxy_url"`
|
||||
}
|
||||
|
||||
type proxyFunc func(req *http.Request) (*url.URL, error)
|
||||
|
||||
func (p *HTTPProxy) Proxy() (proxyFunc, error) {
|
||||
if p.UseSystemProxy {
|
||||
return http.ProxyFromEnvironment, nil
|
||||
} else if len(p.HTTPProxyURL) > 0 {
|
||||
address, err := url.Parse(p.HTTPProxyURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing proxy url %q: %w", p.HTTPProxyURL, err)
|
||||
}
|
||||
return http.ProxyURL(address), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type TCPProxy struct {
|
||||
UseProxy bool `toml:"use_proxy"`
|
||||
ProxyURL string `toml:"proxy_url"`
|
||||
}
|
||||
|
||||
func (p *TCPProxy) Proxy() (*ProxiedDialer, error) {
|
||||
var dialer proxy.Dialer
|
||||
if p.UseProxy {
|
||||
if len(p.ProxyURL) > 0 {
|
||||
parsed, err := url.Parse(p.ProxyURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing proxy url %q: %w", p.ProxyURL, err)
|
||||
}
|
||||
|
||||
if dialer, err = proxy.FromURL(parsed, proxy.Direct); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
dialer = proxy.FromEnvironment()
|
||||
}
|
||||
} else {
|
||||
dialer = proxy.Direct
|
||||
}
|
||||
|
||||
return &ProxiedDialer{dialer}, nil
|
||||
}
|
22
plugins/common/proxy/socks5.go
Normal file
22
plugins/common/proxy/socks5.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type Socks5ProxyConfig struct {
|
||||
Socks5ProxyEnabled bool `toml:"socks5_enabled"`
|
||||
Socks5ProxyAddress string `toml:"socks5_address"`
|
||||
Socks5ProxyUsername string `toml:"socks5_username"`
|
||||
Socks5ProxyPassword string `toml:"socks5_password"`
|
||||
}
|
||||
|
||||
func (c *Socks5ProxyConfig) GetDialer() (proxy.Dialer, error) {
|
||||
var auth *proxy.Auth
|
||||
if c.Socks5ProxyPassword != "" || c.Socks5ProxyUsername != "" {
|
||||
auth = new(proxy.Auth)
|
||||
auth.User = c.Socks5ProxyUsername
|
||||
auth.Password = c.Socks5ProxyPassword
|
||||
}
|
||||
return proxy.SOCKS5("tcp", c.Socks5ProxyAddress, auth, proxy.Direct)
|
||||
}
|
74
plugins/common/proxy/socks5_test.go
Normal file
74
plugins/common/proxy/socks5_test.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-socks5"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSocks5ProxyConfigIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
const (
|
||||
proxyAddress = "127.0.0.1:12345"
|
||||
proxyUsername = "user"
|
||||
proxyPassword = "password"
|
||||
)
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
server, err := socks5.New(&socks5.Config{
|
||||
AuthMethods: []socks5.Authenticator{socks5.UserPassAuthenticator{
|
||||
Credentials: socks5.StaticCredentials{
|
||||
proxyUsername: proxyPassword,
|
||||
},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
go func() {
|
||||
if err := server.ListenAndServe("tcp", proxyAddress); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conf := Socks5ProxyConfig{
|
||||
Socks5ProxyEnabled: true,
|
||||
Socks5ProxyAddress: proxyAddress,
|
||||
Socks5ProxyUsername: proxyUsername,
|
||||
Socks5ProxyPassword: proxyPassword,
|
||||
}
|
||||
dialer, err := conf.GetDialer()
|
||||
require.NoError(t, err)
|
||||
|
||||
var proxyConn net.Conn
|
||||
for i := 0; i < 10; i++ {
|
||||
proxyConn, err = dialer.Dial("tcp", l.Addr().String())
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
require.NotNil(t, proxyConn)
|
||||
defer func() { require.NoError(t, proxyConn.Close()) }()
|
||||
|
||||
serverConn, err := l.Accept()
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, serverConn.Close()) }()
|
||||
|
||||
writePayload := []byte("test")
|
||||
_, err = proxyConn.Write(writePayload)
|
||||
require.NoError(t, err)
|
||||
|
||||
receivePayload := make([]byte, 4)
|
||||
_, err = serverConn.Read(receivePayload)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, writePayload, receivePayload)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue