mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2024-12-30 02:37:01 +00:00
Compare commits
2
Commits
main
...
refactor-proxy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9f56af25f | ||
|
|
8ef439b8d8 |
+3
-3
@@ -189,10 +189,10 @@ func netstack(k *Key) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
if _defaultProxy, err = parseProxy(k.Proxy); err != nil {
|
||||
if _defaultProxy, err = proxy.ParseFromURL(k.Proxy); err != nil {
|
||||
return
|
||||
}
|
||||
proxy.SetDialer(_defaultProxy)
|
||||
proxy.DefaultProxy = _defaultProxy
|
||||
|
||||
if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil {
|
||||
return
|
||||
@@ -236,7 +236,7 @@ func netstack(k *Key) (err error) {
|
||||
log.Infof(
|
||||
"[STACK] %s://%s <-> %s://%s",
|
||||
_defaultDevice.Type(), _defaultDevice.Name(),
|
||||
_defaultProxy.Proto(), _defaultProxy.Addr(),
|
||||
_defaultProxy.Protocol(), _defaultProxy.Address(),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
-114
@@ -1,19 +1,14 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/schema"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/core/device"
|
||||
"github.com/xjasonlyu/tun2socks/v2/core/device/fdbased"
|
||||
"github.com/xjasonlyu/tun2socks/v2/core/device/tun"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
)
|
||||
|
||||
func parseRestAPI(s string) (*url.URL, error) {
|
||||
@@ -69,115 +64,6 @@ func parseFD(u *url.URL, mtu uint32) (device.Device, error) {
|
||||
return fdbased.Open(u.Host, mtu, 0)
|
||||
}
|
||||
|
||||
func parseProxy(s string) (proxy.Proxy, error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = fmt.Sprintf("%s://%s", proto.Socks5 /* default protocol */, s)
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protocol := strings.ToLower(u.Scheme)
|
||||
|
||||
switch protocol {
|
||||
case proto.Direct.String():
|
||||
return proxy.NewDirect(), nil
|
||||
case proto.Reject.String():
|
||||
return proxy.NewReject(), nil
|
||||
case proto.HTTP.String():
|
||||
return parseHTTP(u)
|
||||
case proto.Socks4.String():
|
||||
return parseSocks4(u)
|
||||
case proto.Socks5.String():
|
||||
return parseSocks5(u)
|
||||
case proto.Shadowsocks.String():
|
||||
return parseShadowsocks(u)
|
||||
case proto.Relay.String():
|
||||
return parseRelay(u)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
|
||||
}
|
||||
}
|
||||
|
||||
func parseHTTP(u *url.URL) (proxy.Proxy, error) {
|
||||
address, username := u.Host, u.User.Username()
|
||||
password, _ := u.User.Password()
|
||||
return proxy.NewHTTP(address, username, password)
|
||||
}
|
||||
|
||||
func parseSocks4(u *url.URL) (proxy.Proxy, error) {
|
||||
address, userID := u.Host, u.User.Username()
|
||||
return proxy.NewSocks4(address, userID)
|
||||
}
|
||||
|
||||
func parseSocks5(u *url.URL) (proxy.Proxy, error) {
|
||||
address, username := u.Host, u.User.Username()
|
||||
password, _ := u.User.Password()
|
||||
|
||||
// Socks5 over UDS
|
||||
if address == "" {
|
||||
address = u.Path
|
||||
}
|
||||
return proxy.NewSocks5(address, username, password)
|
||||
}
|
||||
|
||||
func parseShadowsocks(u *url.URL) (proxy.Proxy, error) {
|
||||
var (
|
||||
address = u.Host
|
||||
method, password string
|
||||
obfsMode, obfsHost string
|
||||
)
|
||||
|
||||
if ss := u.User.String(); ss == "" {
|
||||
method = "dummy" // none cipher mode
|
||||
} else if pass, set := u.User.Password(); set {
|
||||
method = u.User.Username()
|
||||
password = pass
|
||||
} else {
|
||||
data, _ := base64.RawURLEncoding.DecodeString(ss)
|
||||
userInfo := strings.SplitN(string(data), ":", 2)
|
||||
if len(userInfo) == 2 {
|
||||
method = userInfo[0]
|
||||
password = userInfo[1]
|
||||
}
|
||||
}
|
||||
|
||||
rawQuery, _ := url.QueryUnescape(u.RawQuery)
|
||||
for _, s := range strings.Split(rawQuery, ";") {
|
||||
data := strings.SplitN(s, "=", 2)
|
||||
if len(data) != 2 {
|
||||
continue
|
||||
}
|
||||
key := data[0]
|
||||
value := data[1]
|
||||
|
||||
switch key {
|
||||
case "obfs":
|
||||
obfsMode = value
|
||||
case "obfs-host":
|
||||
obfsHost = value
|
||||
}
|
||||
}
|
||||
|
||||
return proxy.NewShadowsocks(address, method, password, obfsMode, obfsHost)
|
||||
}
|
||||
|
||||
func parseRelay(u *url.URL) (proxy.Proxy, error) {
|
||||
address, username := u.Host, u.User.Username()
|
||||
password, _ := u.User.Password()
|
||||
|
||||
opts := struct {
|
||||
NoDelay bool
|
||||
}{}
|
||||
if err := schema.NewDecoder().Decode(&opts, u.Query()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return proxy.NewRelay(address, username, password, opts.NoDelay)
|
||||
}
|
||||
|
||||
func parseMulticastGroups(s string) (multicastGroups []net.IP, _ error) {
|
||||
ipStrings := strings.Split(s, ",")
|
||||
for _, ipString := range ipStrings {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/direct"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/http"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/reject"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/relay"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/shadowsocks"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks4"
|
||||
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks5"
|
||||
)
|
||||
@@ -1,33 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Base)(nil)
|
||||
|
||||
type Base struct {
|
||||
addr string
|
||||
proto proto.Proto
|
||||
}
|
||||
|
||||
func (b *Base) Addr() string {
|
||||
return b.addr
|
||||
}
|
||||
|
||||
func (b *Base) Proto() proto.Proto {
|
||||
return b.proto
|
||||
}
|
||||
|
||||
func (b *Base) DialContext(context.Context, *M.Metadata) (net.Conn, error) {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
func (b *Base) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
@@ -1,34 +1,33 @@
|
||||
package proxy
|
||||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Direct)(nil)
|
||||
var _ proxy.Proxy = (*Direct)(nil)
|
||||
|
||||
type Direct struct {
|
||||
*Base
|
||||
}
|
||||
const protocol = "direct"
|
||||
|
||||
func NewDirect() *Direct {
|
||||
return &Direct{
|
||||
Base: &Base{
|
||||
proto: proto.Direct,
|
||||
},
|
||||
}
|
||||
}
|
||||
type Direct struct{ *base.Base }
|
||||
|
||||
func New() *Direct { return &Direct{base.New("", protocol)} }
|
||||
|
||||
func Parse(*url.URL) (proxy.Proxy, error) { return New(), nil }
|
||||
|
||||
func (d *Direct) DialContext(ctx context.Context, metadata *M.Metadata) (net.Conn, error) {
|
||||
c, err := dialer.DialContext(ctx, "tcp", metadata.DestinationAddress())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setKeepAlive(c)
|
||||
internal.SetKeepAlive(c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -55,3 +54,7 @@ func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
}
|
||||
return pc.PacketConn.WriteTo(b, udpAddr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package proxy
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -13,36 +13,45 @@ import (
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
)
|
||||
|
||||
var _ proxy.Proxy = (*HTTP)(nil)
|
||||
|
||||
const protocol = "http"
|
||||
|
||||
type HTTP struct {
|
||||
*Base
|
||||
*base.Base
|
||||
|
||||
user string
|
||||
pass string
|
||||
}
|
||||
|
||||
func NewHTTP(addr, user, pass string) (*HTTP, error) {
|
||||
func New(addr, user, pass string) (*HTTP, error) {
|
||||
return &HTTP{
|
||||
Base: &Base{
|
||||
addr: addr,
|
||||
proto: proto.HTTP,
|
||||
},
|
||||
Base: base.New(addr, protocol),
|
||||
user: user,
|
||||
pass: pass,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
|
||||
address, username := proxyURL.Host, proxyURL.User.Username()
|
||||
password, _ := proxyURL.User.Password()
|
||||
return New(address, username, password)
|
||||
}
|
||||
|
||||
func (h *HTTP) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", h.Addr())
|
||||
c, err = dialer.DialContext(ctx, "tcp", h.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", h.Addr(), err)
|
||||
return nil, fmt.Errorf("connect to %s: %w", h.Address(), err)
|
||||
}
|
||||
setKeepAlive(c)
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
internal.SafeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
err = h.shakeHand(metadata, c)
|
||||
@@ -98,3 +107,7 @@ func basicAuth(username, password string) string {
|
||||
auth := username + ":" + password
|
||||
return base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
)
|
||||
|
||||
var _ proxy.Proxy = (*Base)(nil)
|
||||
|
||||
type Base struct {
|
||||
address, protocol string
|
||||
}
|
||||
|
||||
func New(address, protocol string) *Base {
|
||||
return &Base{
|
||||
address: address,
|
||||
protocol: protocol,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Base) Address() string {
|
||||
return b.address
|
||||
}
|
||||
|
||||
func (b *Base) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Base) String() string {
|
||||
return fmt.Sprintf("%s://%s", b.protocol, b.address)
|
||||
}
|
||||
|
||||
func (b *Base) DialContext(context.Context, *M.Metadata) (net.Conn, error) {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
func (b *Base) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/socks5"
|
||||
)
|
||||
|
||||
const tcpKeepAlivePeriod = 30 * time.Second
|
||||
|
||||
// SetKeepAlive sets the tcp keepalive option for the tcp connection.
|
||||
func SetKeepAlive(c net.Conn) {
|
||||
if tcp, ok := c.(*net.TCPConn); ok {
|
||||
tcp.SetKeepAlive(true)
|
||||
tcp.SetKeepAlivePeriod(tcpKeepAlivePeriod)
|
||||
}
|
||||
}
|
||||
|
||||
// SafeConnClose closes the given tcp connection safely.
|
||||
func SafeConnClose(c net.Conn, err error) {
|
||||
if c != nil && err != nil {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeSocksAddr serializes *metadata.Metadata to socks5.Addr.
|
||||
func SerializeSocksAddr(m *M.Metadata) socks5.Addr {
|
||||
return socks5.SerializeAddr("", m.DstIP, m.DstPort)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package proto
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
Direct Proto = iota
|
||||
Reject
|
||||
HTTP
|
||||
Socks4
|
||||
Socks5
|
||||
Shadowsocks
|
||||
Relay
|
||||
)
|
||||
|
||||
type Proto uint8
|
||||
|
||||
func (proto Proto) String() string {
|
||||
switch proto {
|
||||
case Direct:
|
||||
return "direct"
|
||||
case Reject:
|
||||
return "reject"
|
||||
case HTTP:
|
||||
return "http"
|
||||
case Socks4:
|
||||
return "socks4"
|
||||
case Socks5:
|
||||
return "socks5"
|
||||
case Shadowsocks:
|
||||
return "ss"
|
||||
case Relay:
|
||||
return "relay"
|
||||
default:
|
||||
return fmt.Sprintf("proto(%d)", proto)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
// ErrProtocol indicates that parsing encountered an unknown protocol.
|
||||
var ErrProtocol = errors.New("proxy: unknown protocol")
|
||||
|
||||
// A protocol holds a proxy protocol's name and how to parse it.
|
||||
type protocol struct {
|
||||
name string
|
||||
parse func(*url.URL) (Proxy, error)
|
||||
}
|
||||
|
||||
// Protocols is the list of registered proxy protocols.
|
||||
var (
|
||||
protocolsMu sync.Mutex
|
||||
atomicProtocols atomic.Value
|
||||
)
|
||||
|
||||
// RegisterProtocol registers a proxy protocol for use by [Parse].
|
||||
// Name is the name of the proxy protocol, like "http" or "socks5".
|
||||
// [Parse] is the function that parses the proxy url.
|
||||
func RegisterProtocol(name string, parse func(*url.URL) (Proxy, error)) {
|
||||
protocolsMu.Lock()
|
||||
formats, _ := atomicProtocols.Load().([]protocol)
|
||||
atomicProtocols.Store(append(formats, protocol{name, parse}))
|
||||
protocolsMu.Unlock()
|
||||
}
|
||||
|
||||
// pick determines the protocol by the given name.
|
||||
func pick(name string) protocol {
|
||||
protocols, _ := atomicProtocols.Load().([]protocol)
|
||||
for _, p := range protocols {
|
||||
if p.name == name {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return protocol{}
|
||||
}
|
||||
|
||||
// Parse parses proxy *url.URL that holds the proxy info into Proxy.
|
||||
// Protocol registration is typically done by an init function in the
|
||||
// proxy-specific package.
|
||||
func Parse(proxyURL *url.URL) (Proxy, error) {
|
||||
if proxyURL == nil {
|
||||
return nil, errors.New("proxy: nil url")
|
||||
}
|
||||
if proxyURL.Scheme == "" {
|
||||
return nil, errors.New("proxy: protocol not specified")
|
||||
}
|
||||
p := pick(proxyURL.Scheme)
|
||||
if p.parse == nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrProtocol, proxyURL.Scheme)
|
||||
}
|
||||
return p.parse(proxyURL)
|
||||
}
|
||||
|
||||
// ParseFromURL parses url string that holds the proxy info into Proxy.
|
||||
func ParseFromURL(proxy string) (Proxy, error) {
|
||||
proxyURL, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Parse(proxyURL)
|
||||
}
|
||||
+23
-22
@@ -7,44 +7,45 @@ import (
|
||||
"time"
|
||||
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpConnectTimeout = 5 * time.Second
|
||||
TCPConnectTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var _defaultDialer Dialer = &Base{}
|
||||
// DefaultProxy is the default [Proxy] and is used by [Dial], [DialContext], and [DialUDP].
|
||||
var DefaultProxy Proxy = nil
|
||||
|
||||
type Dialer interface {
|
||||
type Proxy interface {
|
||||
// Address returns the address of the proxy.
|
||||
Address() string
|
||||
|
||||
// Protocol returns the protocol of the proxy.
|
||||
Protocol() string
|
||||
|
||||
// String returns the string representation of the proxy.
|
||||
String() string
|
||||
|
||||
// DialContext is used to dial TCP networks with context.
|
||||
DialContext(context.Context, *M.Metadata) (net.Conn, error)
|
||||
|
||||
// DialUDP is used to to dial/listen UDP networks.
|
||||
DialUDP(*M.Metadata) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
type Proxy interface {
|
||||
Dialer
|
||||
Addr() string
|
||||
Proto() proto.Proto
|
||||
}
|
||||
|
||||
// SetDialer sets default Dialer.
|
||||
func SetDialer(d Dialer) {
|
||||
_defaultDialer = d
|
||||
}
|
||||
|
||||
// Dial uses default Dialer to dial TCP.
|
||||
// Dial uses the DefaultProxy to dial TCP.
|
||||
func Dial(metadata *M.Metadata) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), TCPConnectTimeout)
|
||||
defer cancel()
|
||||
return _defaultDialer.DialContext(ctx, metadata)
|
||||
return DialContext(ctx, metadata)
|
||||
}
|
||||
|
||||
// DialContext uses default Dialer to dial TCP with context.
|
||||
// DialContext uses the DefaultProxy to dial TCP with context.
|
||||
func DialContext(ctx context.Context, metadata *M.Metadata) (net.Conn, error) {
|
||||
return _defaultDialer.DialContext(ctx, metadata)
|
||||
return DefaultProxy.DialContext(ctx, metadata)
|
||||
}
|
||||
|
||||
// DialUDP uses default Dialer to dial UDP.
|
||||
// DialUDP uses the DefaultProxy to dial UDP.
|
||||
func DialUDP(metadata *M.Metadata) (net.PacketConn, error) {
|
||||
return _defaultDialer.DialUDP(metadata)
|
||||
return DefaultProxy.DialUDP(metadata)
|
||||
}
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
package proxy
|
||||
package reject
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Reject)(nil)
|
||||
var _ proxy.Proxy = (*Reject)(nil)
|
||||
|
||||
type Reject struct {
|
||||
*Base
|
||||
}
|
||||
const protocol = "reject"
|
||||
|
||||
func NewReject() *Reject {
|
||||
return &Reject{
|
||||
Base: &Base{
|
||||
proto: proto.Reject,
|
||||
},
|
||||
}
|
||||
}
|
||||
type Reject struct{ *base.Base }
|
||||
|
||||
func (r *Reject) DialContext(context.Context, *M.Metadata) (net.Conn, error) {
|
||||
return &nopConn{}, nil
|
||||
}
|
||||
func New() *Reject { return &Reject{base.New("", protocol)} }
|
||||
|
||||
func (r *Reject) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
||||
return &nopPacketConn{}, nil
|
||||
}
|
||||
func Parse(*url.URL) (proxy.Proxy, error) { return New(), nil }
|
||||
|
||||
func (r *Reject) DialContext(context.Context, *M.Metadata) (net.Conn, error) { return &nopConn{}, nil }
|
||||
|
||||
func (r *Reject) DialUDP(*M.Metadata) (net.PacketConn, error) { return &nopPacketConn{}, nil }
|
||||
|
||||
type nopConn struct{}
|
||||
|
||||
@@ -52,3 +46,7 @@ func (npc *nopPacketConn) LocalAddr() net.Addr { ret
|
||||
func (npc *nopPacketConn) SetDeadline(time.Time) error { return nil }
|
||||
func (npc *nopPacketConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (npc *nopPacketConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package proxy
|
||||
package relay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,20 +9,26 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/relay"
|
||||
"github.com/gorilla/schema"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/common/pool"
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Relay)(nil)
|
||||
var _ proxy.Proxy = (*Relay)(nil)
|
||||
|
||||
const protocol = "relay"
|
||||
|
||||
type Relay struct {
|
||||
*Base
|
||||
*base.Base
|
||||
|
||||
user string
|
||||
pass string
|
||||
@@ -30,24 +36,35 @@ type Relay struct {
|
||||
noDelay bool
|
||||
}
|
||||
|
||||
func NewRelay(addr, user, pass string, noDelay bool) (*Relay, error) {
|
||||
func New(addr, user, pass string, noDelay bool) (*Relay, error) {
|
||||
return &Relay{
|
||||
Base: &Base{
|
||||
addr: addr,
|
||||
proto: proto.Relay,
|
||||
},
|
||||
Base: base.New(addr, protocol),
|
||||
user: user,
|
||||
pass: pass,
|
||||
noDelay: noDelay,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
|
||||
address, username := proxyURL.Host, proxyURL.User.Username()
|
||||
password, _ := proxyURL.User.Password()
|
||||
|
||||
opts := struct {
|
||||
NoDelay bool
|
||||
}{}
|
||||
if err := schema.NewDecoder().Decode(&opts, proxyURL.Query()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return New(address, username, password, opts.NoDelay)
|
||||
}
|
||||
|
||||
func (rl *Relay) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
return rl.dialContext(ctx, metadata)
|
||||
}
|
||||
|
||||
func (rl *Relay) DialUDP(metadata *M.Metadata) (net.PacketConn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), proxy.TCPConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
return rl.dialContext(ctx, metadata)
|
||||
@@ -56,14 +73,14 @@ func (rl *Relay) DialUDP(metadata *M.Metadata) (net.PacketConn, error) {
|
||||
func (rl *Relay) dialContext(ctx context.Context, metadata *M.Metadata) (rc *relayConn, err error) {
|
||||
var c net.Conn
|
||||
|
||||
c, err = dialer.DialContext(ctx, "tcp", rl.Addr())
|
||||
c, err = dialer.DialContext(ctx, "tcp", rl.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", rl.Addr(), err)
|
||||
return nil, fmt.Errorf("connect to %s: %w", rl.Address(), err)
|
||||
}
|
||||
setKeepAlive(c)
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
internal.SafeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
req := relay.Request{
|
||||
@@ -250,3 +267,7 @@ func serializeRelayAddr(m *M.Metadata) *relay.AddrFeature {
|
||||
}
|
||||
return af
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -1,23 +1,30 @@
|
||||
package proxy
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/shadowsocks/core"
|
||||
obfs "github.com/xjasonlyu/tun2socks/v2/transport/simple-obfs"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/socks5"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Shadowsocks)(nil)
|
||||
var _ proxy.Proxy = (*Shadowsocks)(nil)
|
||||
|
||||
const protocol = "ss"
|
||||
|
||||
type Shadowsocks struct {
|
||||
*Base
|
||||
*base.Base
|
||||
|
||||
cipher core.Cipher
|
||||
|
||||
@@ -25,44 +32,82 @@ type Shadowsocks struct {
|
||||
obfsMode, obfsHost string
|
||||
}
|
||||
|
||||
func NewShadowsocks(addr, method, password, obfsMode, obfsHost string) (*Shadowsocks, error) {
|
||||
func New(addr, method, password, obfsMode, obfsHost string) (*Shadowsocks, error) {
|
||||
cipher, err := core.PickCipher(method, nil, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ss initialize: %w", err)
|
||||
}
|
||||
|
||||
return &Shadowsocks{
|
||||
Base: &Base{
|
||||
addr: addr,
|
||||
proto: proto.Shadowsocks,
|
||||
},
|
||||
Base: base.New(addr, protocol),
|
||||
cipher: cipher,
|
||||
obfsMode: obfsMode,
|
||||
obfsHost: obfsHost,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ss *Shadowsocks) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", ss.Addr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||
func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
|
||||
var (
|
||||
address = proxyURL.Host
|
||||
method, password string
|
||||
obfsMode, obfsHost string
|
||||
)
|
||||
|
||||
if ss := proxyURL.User.String(); ss == "" {
|
||||
method = "dummy" // none cipher mode
|
||||
} else if pass, set := proxyURL.User.Password(); set {
|
||||
method = proxyURL.User.Username()
|
||||
password = pass
|
||||
} else {
|
||||
data, _ := base64.RawURLEncoding.DecodeString(ss)
|
||||
userInfo := strings.SplitN(string(data), ":", 2)
|
||||
if len(userInfo) == 2 {
|
||||
method = userInfo[0]
|
||||
password = userInfo[1]
|
||||
}
|
||||
}
|
||||
setKeepAlive(c)
|
||||
|
||||
rawQuery, _ := url.QueryUnescape(proxyURL.RawQuery)
|
||||
for _, s := range strings.Split(rawQuery, ";") {
|
||||
data := strings.SplitN(s, "=", 2)
|
||||
if len(data) != 2 {
|
||||
continue
|
||||
}
|
||||
key := data[0]
|
||||
value := data[1]
|
||||
|
||||
switch key {
|
||||
case "obfs":
|
||||
obfsMode = value
|
||||
case "obfs-host":
|
||||
obfsHost = value
|
||||
}
|
||||
}
|
||||
|
||||
return New(address, method, password, obfsMode, obfsHost)
|
||||
}
|
||||
|
||||
func (ss *Shadowsocks) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", ss.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Address(), err)
|
||||
}
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
internal.SafeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
switch ss.obfsMode {
|
||||
case "tls":
|
||||
c = obfs.NewTLSObfs(c, ss.obfsHost)
|
||||
case "http":
|
||||
_, port, _ := net.SplitHostPort(ss.addr)
|
||||
_, port, _ := net.SplitHostPort(ss.Address())
|
||||
c = obfs.NewHTTPObfs(c, ss.obfsHost, port)
|
||||
}
|
||||
|
||||
c = ss.cipher.StreamConn(c)
|
||||
_, err = c.Write(serializeSocksAddr(metadata))
|
||||
_, err = c.Write(internal.SerializeSocksAddr(metadata))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -72,9 +117,9 @@ func (ss *Shadowsocks) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
||||
return nil, fmt.Errorf("listen packet: %w", err)
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr())
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", ss.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err)
|
||||
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Address(), err)
|
||||
}
|
||||
|
||||
pc = ss.cipher.PacketConn(pc)
|
||||
@@ -90,7 +135,7 @@ type ssPacketConn struct {
|
||||
func (pc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
var packet []byte
|
||||
if ma, ok := addr.(*M.Addr); ok {
|
||||
packet, err = socks5.EncodeUDPPacket(serializeSocksAddr(ma.Metadata()), b)
|
||||
packet, err = socks5.EncodeUDPPacket(internal.SerializeSocksAddr(ma.Metadata()), b)
|
||||
} else {
|
||||
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddr(addr), b)
|
||||
}
|
||||
@@ -120,3 +165,7 @@ func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
copy(b, b[len(addr):])
|
||||
return n - len(addr), udpAddr, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/socks4"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Socks4)(nil)
|
||||
|
||||
type Socks4 struct {
|
||||
*Base
|
||||
|
||||
userID string
|
||||
}
|
||||
|
||||
func NewSocks4(addr, userID string) (*Socks4, error) {
|
||||
return &Socks4{
|
||||
Base: &Base{
|
||||
addr: addr,
|
||||
proto: proto.Socks4,
|
||||
},
|
||||
userID: userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ss *Socks4) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", ss.Addr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||
}
|
||||
setKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
err = socks4.ClientHandshake(c, metadata.DestinationAddress(), socks4.CmdConnect, ss.userID)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package socks4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/socks4"
|
||||
)
|
||||
|
||||
var _ proxy.Proxy = (*Socks4)(nil)
|
||||
|
||||
const protocol = "socks4"
|
||||
|
||||
type Socks4 struct {
|
||||
*base.Base
|
||||
|
||||
userID string
|
||||
}
|
||||
|
||||
func New(addr, userID string) (*Socks4, error) {
|
||||
return &Socks4{
|
||||
Base: base.New(addr, protocol),
|
||||
userID: userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
|
||||
address, userID := proxyURL.Host, proxyURL.User.Username()
|
||||
return New(address, userID)
|
||||
}
|
||||
|
||||
func (ss *Socks4) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", ss.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Address(), err)
|
||||
}
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
internal.SafeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
err = socks4.ClientHandshake(c, metadata.DestinationAddress(), socks4.CmdConnect, ss.userID)
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package proxy
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,17 +6,22 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/dialer"
|
||||
M "github.com/xjasonlyu/tun2socks/v2/metadata"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
|
||||
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
|
||||
"github.com/xjasonlyu/tun2socks/v2/transport/socks5"
|
||||
)
|
||||
|
||||
var _ Proxy = (*Socks5)(nil)
|
||||
var _ proxy.Proxy = (*Socks5)(nil)
|
||||
|
||||
const protocol = "socks5"
|
||||
|
||||
type Socks5 struct {
|
||||
*Base
|
||||
*base.Base
|
||||
|
||||
user string
|
||||
pass string
|
||||
@@ -25,32 +30,40 @@ type Socks5 struct {
|
||||
unix bool
|
||||
}
|
||||
|
||||
func NewSocks5(addr, user, pass string) (*Socks5, error) {
|
||||
func New(addr, user, pass string) (*Socks5, error) {
|
||||
return &Socks5{
|
||||
Base: &Base{
|
||||
addr: addr,
|
||||
proto: proto.Socks5,
|
||||
},
|
||||
Base: base.New(addr, protocol),
|
||||
user: user,
|
||||
pass: pass,
|
||||
unix: len(addr) > 0 && addr[0] == '/',
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
|
||||
address, username := proxyURL.Host, proxyURL.User.Username()
|
||||
password, _ := proxyURL.User.Password()
|
||||
|
||||
// Socks5 over UDS
|
||||
if address == "" {
|
||||
address = proxyURL.Path
|
||||
}
|
||||
return New(address, username, password)
|
||||
}
|
||||
|
||||
func (ss *Socks5) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
network := "tcp"
|
||||
if ss.unix {
|
||||
network = "unix"
|
||||
}
|
||||
|
||||
c, err = dialer.DialContext(ctx, network, ss.Addr())
|
||||
c, err = dialer.DialContext(ctx, network, ss.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Address(), err)
|
||||
}
|
||||
setKeepAlive(c)
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
internal.SafeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
var user *socks5.User
|
||||
@@ -61,7 +74,7 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *M.Metadata) (c net.
|
||||
}
|
||||
}
|
||||
|
||||
_, err = socks5.ClientHandshake(c, serializeSocksAddr(metadata), socks5.CmdConnect, user)
|
||||
_, err = socks5.ClientHandshake(c, internal.SerializeSocksAddr(metadata), socks5.CmdConnect, user)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,15 +83,15 @@ func (ss *Socks5) DialUDP(*M.Metadata) (_ net.PacketConn, err error) {
|
||||
return nil, fmt.Errorf("%w when unix domain socket is enabled", errors.ErrUnsupported)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), proxy.TCPConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
c, err := dialer.DialContext(ctx, "tcp", ss.Addr())
|
||||
c, err := dialer.DialContext(ctx, "tcp", ss.Address())
|
||||
if err != nil {
|
||||
err = fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||
err = fmt.Errorf("connect to %s: %w", ss.Address(), err)
|
||||
return
|
||||
}
|
||||
setKeepAlive(c)
|
||||
internal.SetKeepAlive(c)
|
||||
|
||||
defer func() {
|
||||
if err != nil && c != nil {
|
||||
@@ -128,9 +141,9 @@ func (ss *Socks5) DialUDP(*M.Metadata) (_ net.PacketConn, err error) {
|
||||
}
|
||||
|
||||
if bindAddr.IP.IsUnspecified() { /* e.g. "0.0.0.0" or "::" */
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr())
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", ss.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err)
|
||||
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Address(), err)
|
||||
}
|
||||
bindAddr.IP = udpAddr.IP
|
||||
}
|
||||
@@ -148,7 +161,7 @@ type socksPacketConn struct {
|
||||
func (pc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
var packet []byte
|
||||
if ma, ok := addr.(*M.Addr); ok {
|
||||
packet, err = socks5.EncodeUDPPacket(serializeSocksAddr(ma.Metadata()), b)
|
||||
packet, err = socks5.EncodeUDPPacket(internal.SerializeSocksAddr(ma.Metadata()), b)
|
||||
} else {
|
||||
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddr(addr), b)
|
||||
}
|
||||
@@ -185,6 +198,6 @@ func (pc *socksPacketConn) Close() error {
|
||||
return pc.PacketConn.Close()
|
||||
}
|
||||
|
||||
func serializeSocksAddr(m *M.Metadata) socks5.Addr {
|
||||
return socks5.SerializeAddr("", m.DstIP, m.DstPort)
|
||||
func init() {
|
||||
proxy.RegisterProtocol(protocol, Parse)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URL is the universal representation of the proxy configuration.
|
||||
type URL url.URL
|
||||
|
||||
func (u *URL) Protocol() string {
|
||||
return u.Scheme
|
||||
}
|
||||
|
||||
func (u *URL) Address() string {
|
||||
return u.Host
|
||||
}
|
||||
|
||||
func (u *URL) String() string {
|
||||
return (&url.URL{
|
||||
Scheme: u.Scheme,
|
||||
Host: u.Host,
|
||||
Path: strings.TrimRight(u.Path, "/"),
|
||||
}).String()
|
||||
}
|
||||
|
||||
func ParseURL(rawURL string) (*URL, error) {
|
||||
proxyURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if proxyURL.Scheme == "" {
|
||||
return nil, errors.New("proxy: protocol not specified")
|
||||
}
|
||||
return (*URL)(proxyURL), nil
|
||||
}
|
||||
|
||||
func MustParseURL(rawURL string) *URL {
|
||||
u, err := ParseURL(rawURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type URLTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (s *URLTestSuite) TestAddress() {
|
||||
tests := []struct {
|
||||
u *URL
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
MustParseURL("http://example.com/"),
|
||||
"http://example.com",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s.Assert().Equal(tt.expected, tt.u.String())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestURLTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(URLTestSuite))
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpKeepAlivePeriod = 30 * time.Second
|
||||
)
|
||||
|
||||
// setKeepAlive sets tcp keepalive option for tcp connection.
|
||||
func setKeepAlive(c net.Conn) {
|
||||
if tcp, ok := c.(*net.TCPConn); ok {
|
||||
tcp.SetKeepAlive(true)
|
||||
tcp.SetKeepAlivePeriod(tcpKeepAlivePeriod)
|
||||
}
|
||||
}
|
||||
|
||||
// safeConnClose closes tcp connection safely.
|
||||
func safeConnClose(c net.Conn, err error) {
|
||||
if c != nil && err != nil {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user