Fix(socks5): fully udp associate support (#233)

This commit is contained in:
X. Jason Lyu
2019-07-25 17:47:39 +08:00
committed by Dreamacro
parent 183e776970
commit 1fd8f690fe
13 changed files with 483 additions and 89 deletions

View File

@ -92,13 +92,13 @@ func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr,
return nil, nil, err
}
remoteAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.DstPort))
targetAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.DstPort))
if err != nil {
return nil, nil, err
}
pc = ss.cipher.PacketConn(pc)
return &ssUDPConn{PacketConn: pc, rAddr: remoteAddr}, addr, nil
return &ssUDPConn{PacketConn: pc, rAddr: targetAddr}, addr, nil
}
func (ss *ShadowSocks) MarshalJSON() ([]byte, error) {

View File

@ -3,6 +3,8 @@ package adapters
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"strconv"
@ -51,24 +53,31 @@ func (ss *Socks5) Dial(metadata *C.Metadata) (net.Conn, error) {
Password: ss.pass,
}
}
if err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil {
if _, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil {
return nil, err
}
return c, nil
}
func (ss *Socks5) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
func (ss *Socks5) DialUDP(metadata *C.Metadata) (_ net.PacketConn, _ net.Addr, err error) {
c, err := dialTimeout("tcp", ss.addr, tcpTimeout)
if err != nil {
err = fmt.Errorf("%s connect error", ss.addr)
return
}
if err == nil && ss.tls {
if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
err = cc.Handshake()
c = cc
}
if err != nil {
return nil, nil, fmt.Errorf("%s connect error", ss.addr)
}
defer func() {
if err != nil {
c.Close()
}
}()
tcpKeepAlive(c)
var user *socks5.User
if ss.user != "" {
@ -78,10 +87,36 @@ func (ss *Socks5) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error
}
}
if err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user); err != nil {
return nil, nil, err
bindAddr, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user)
if err != nil {
err = fmt.Errorf("%v client hanshake error", err)
return
}
return &fakeUDPConn{Conn: c}, c.LocalAddr(), nil
addr, err := net.ResolveUDPAddr("udp", bindAddr.String())
if err != nil {
return
}
targetAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.DstPort))
if err != nil {
return
}
pc, err := net.ListenPacket("udp", "")
if err != nil {
return
}
go func() {
io.Copy(ioutil.Discard, c)
c.Close()
// A UDP association terminates when the TCP connection that the UDP
// ASSOCIATE request arrived on terminates. RFC1928
pc.Close()
}()
return &socksUDPConn{PacketConn: pc, rAddr: targetAddr}, addr, nil
}
func NewSocks5(option Socks5Option) *Socks5 {
@ -108,3 +143,26 @@ func NewSocks5(option Socks5Option) *Socks5 {
tlsConfig: tlsConfig,
}
}
type socksUDPConn struct {
net.PacketConn
rAddr net.Addr
}
func (uc *socksUDPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
packet, err := socks5.EncodeUDPPacket(uc.rAddr.String(), b)
if err != nil {
return
}
return uc.PacketConn.WriteTo(packet, addr)
}
func (uc *socksUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, a, e := uc.PacketConn.ReadFrom(b)
addr, payload, err := socks5.DecodeUDPPacket(b)
if err != nil {
return 0, nil, err
}
copy(b, payload)
return n - len(addr) - 3, a, e
}

View File

@ -48,7 +48,10 @@ func (v *Vmess) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error)
}
tcpKeepAlive(c)
c, err = v.client.New(c, parseVmessAddr(metadata))
return &fakeUDPConn{Conn: c}, c.LocalAddr(), err
if err != nil {
return nil, nil, fmt.Errorf("new vmess client error: %v", err)
}
return &fakeUDPConn{Conn: c}, c.RemoteAddr(), nil
}
func NewVmess(option VmessOption) (*Vmess, error) {
@ -74,7 +77,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
Base: &Base{
name: option.Name,
tp: C.Vmess,
udp: option.UDP,
udp: true,
},
server: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
client: client,