2022-04-29 13:38:49 -04:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
2022-05-02 11:36:24 -04:00
|
|
|
"context"
|
2022-04-29 13:38:49 -04:00
|
|
|
"fmt"
|
2022-05-02 11:36:24 -04:00
|
|
|
"net"
|
2022-04-29 13:38:49 -04:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"golang.org/x/net/proxy"
|
|
|
|
)
|
|
|
|
|
2022-05-01 23:28:54 -04:00
|
|
|
func GetTransport(proxy string) (http.RoundTripper, error) {
|
2022-05-01 19:13:27 -04:00
|
|
|
if proxy == "" {
|
2022-05-01 23:28:54 -04:00
|
|
|
return http.DefaultTransport, nil
|
2022-05-01 19:13:27 -04:00
|
|
|
}
|
2022-06-21 21:32:16 -04:00
|
|
|
proxyURL, err := url.Parse(proxy)
|
2022-04-29 13:38:49 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parse proxyUrl, url:%s, err: %w", proxy, err)
|
|
|
|
}
|
2022-06-21 21:32:16 -04:00
|
|
|
if proxyURL.Host == "unix" && proxyURL.Scheme == "socks5" &&
|
|
|
|
len(proxyURL.Path) > 1 /* Path cannot be empty or just / */ {
|
|
|
|
return unixSocks5Proxy(proxyURL)
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|
2022-06-21 21:32:16 -04:00
|
|
|
return proxyTCP(proxyURL)
|
2022-05-02 11:36:24 -04:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:28:53 -04:00
|
|
|
type forwardDialer func(ctx context.Context, network, address string) (net.Conn, error)
|
2022-05-02 11:36:24 -04:00
|
|
|
|
2022-05-31 17:28:53 -04:00
|
|
|
func (d forwardDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
|
|
return d(ctx, network, address)
|
2022-05-02 11:36:24 -04:00
|
|
|
}
|
2022-05-31 17:28:53 -04:00
|
|
|
func (d forwardDialer) Dial(network, address string) (net.Conn, error) {
|
2022-05-02 11:36:24 -04:00
|
|
|
panic("Dial should not be called")
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|
|
|
|
|
2022-06-21 21:32:16 -04:00
|
|
|
func unixSocks5Proxy(proxyURL *url.URL) (http.RoundTripper, error) {
|
2022-05-02 11:36:24 -04:00
|
|
|
trans := defaultTransport().Clone()
|
|
|
|
if trans.DialContext == nil {
|
|
|
|
panic("DefaultTransport has nil DialContext")
|
|
|
|
}
|
2022-05-23 12:45:25 -04:00
|
|
|
var auth *proxy.Auth
|
2022-06-21 21:32:16 -04:00
|
|
|
username := proxyURL.User.Username()
|
|
|
|
password, _ := proxyURL.User.Password()
|
2022-05-23 12:45:25 -04:00
|
|
|
// Both username and password should have atleast one char
|
|
|
|
if username != "" && password != "" {
|
|
|
|
auth = &proxy.Auth{
|
|
|
|
User: username,
|
|
|
|
Password: password,
|
|
|
|
}
|
|
|
|
}
|
2022-06-21 21:32:16 -04:00
|
|
|
dialer, err := proxy.SOCKS5("unix", proxyURL.Path, auth, forwardDialer(trans.DialContext))
|
2022-04-29 13:38:49 -04:00
|
|
|
if err != nil {
|
2022-06-21 21:32:16 -04:00
|
|
|
return nil, fmt.Errorf("failed to make socks proxy, url: %s, err: %w", proxyURL, err)
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|
|
|
|
ctxDialer, ok := dialer.(proxy.ContextDialer)
|
|
|
|
if !ok {
|
2022-05-01 19:13:27 -04:00
|
|
|
panic("proxy.SOCKS5 did not return a ContextDialer")
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|
2022-05-01 19:13:27 -04:00
|
|
|
trans.DialContext = ctxDialer.DialContext
|
|
|
|
trans.Proxy = nil
|
2022-05-01 23:28:54 -04:00
|
|
|
return trans, nil
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|
|
|
|
|
2022-06-21 21:32:16 -04:00
|
|
|
func proxyTCP(proxyURL *url.URL) (http.RoundTripper, error) {
|
2022-05-02 11:36:24 -04:00
|
|
|
trans := defaultTransport().Clone()
|
2022-06-21 21:32:16 -04:00
|
|
|
trans.Proxy = http.ProxyURL(proxyURL)
|
2022-05-01 23:28:54 -04:00
|
|
|
return trans, nil
|
2022-05-01 19:13:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func defaultTransport() *http.Transport {
|
|
|
|
transPtr, ok := http.DefaultTransport.(*http.Transport)
|
|
|
|
if !ok {
|
|
|
|
panic("http.DefaultTransport is not a *http.Transport")
|
|
|
|
}
|
2022-05-02 11:36:24 -04:00
|
|
|
return transPtr
|
2022-04-29 13:38:49 -04:00
|
|
|
}
|