mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2024-12-30 02:37:01 +00:00
172 lines
3.9 KiB
Go
172 lines
3.9 KiB
Go
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"
|
|
"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.Proxy = (*Shadowsocks)(nil)
|
|
|
|
const protocol = "ss"
|
|
|
|
type Shadowsocks struct {
|
|
*base.Base
|
|
|
|
cipher core.Cipher
|
|
|
|
// simple-obfs plugin
|
|
obfsMode, obfsHost string
|
|
}
|
|
|
|
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.New(addr, protocol),
|
|
cipher: cipher,
|
|
obfsMode: obfsMode,
|
|
obfsHost: obfsHost,
|
|
}, nil
|
|
}
|
|
|
|
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]
|
|
}
|
|
}
|
|
|
|
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) {
|
|
internal.SafeConnClose(c, err)
|
|
}(c)
|
|
|
|
switch ss.obfsMode {
|
|
case "tls":
|
|
c = obfs.NewTLSObfs(c, ss.obfsHost)
|
|
case "http":
|
|
_, port, _ := net.SplitHostPort(ss.Address())
|
|
c = obfs.NewHTTPObfs(c, ss.obfsHost, port)
|
|
}
|
|
|
|
c = ss.cipher.StreamConn(c)
|
|
_, err = c.Write(internal.SerializeSocksAddr(metadata))
|
|
return
|
|
}
|
|
|
|
func (ss *Shadowsocks) DialUDP(*M.Metadata) (net.PacketConn, error) {
|
|
pc, err := dialer.ListenPacket("udp", "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listen packet: %w", err)
|
|
}
|
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", ss.Address())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Address(), err)
|
|
}
|
|
|
|
pc = ss.cipher.PacketConn(pc)
|
|
return &ssPacketConn{PacketConn: pc, rAddr: udpAddr}, nil
|
|
}
|
|
|
|
type ssPacketConn struct {
|
|
net.PacketConn
|
|
|
|
rAddr net.Addr
|
|
}
|
|
|
|
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(internal.SerializeSocksAddr(ma.Metadata()), b)
|
|
} else {
|
|
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddr(addr), b)
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
return pc.PacketConn.WriteTo(packet[3:], pc.rAddr)
|
|
}
|
|
|
|
func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
n, _, err := pc.PacketConn.ReadFrom(b)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
|
|
addr := socks5.SplitAddr(b[:n])
|
|
if addr == nil {
|
|
return 0, nil, errors.New("parse addr error")
|
|
}
|
|
|
|
udpAddr := addr.UDPAddr()
|
|
if udpAddr == nil {
|
|
return 0, nil, errors.New("parse addr error")
|
|
}
|
|
|
|
copy(b, b[len(addr):])
|
|
return n - len(addr), udpAddr, err
|
|
}
|
|
|
|
func init() {
|
|
proxy.RegisterProtocol(protocol, Parse)
|
|
}
|