1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View 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
}

View 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)
}

View 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
}

View 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)
}

View 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)
}