You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
4.1 KiB

4 years ago
package proxy
import (
"context"
"errors"
4 years ago
"fmt"
"io"
"net"
4 years ago
"github.com/xjasonlyu/tun2socks/component/dialer"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
"github.com/xjasonlyu/tun2socks/transport/socks5"
4 years ago
)
var _ Proxy = (*Socks5)(nil)
4 years ago
type Socks5 struct {
*Base
user string
pass string
// unix indicates if socks5 over UDS is enabled.
unix bool
4 years ago
}
4 years ago
func NewSocks5(addr, user, pass string) (*Socks5, error) {
4 years ago
return &Socks5{
Base: &Base{
addr: addr,
proto: proto.Socks5,
},
4 years ago
user: user,
pass: pass,
unix: len(addr) > 0 && addr[0] == '/',
4 years ago
}, nil
}
func (ss *Socks5) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
var network = "tcp"
if ss.unix {
network = "unix"
}
c, err = dialer.DialContext(ctx, network, ss.Addr())
4 years ago
if err != nil {
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
}
4 years ago
setKeepAlive(c)
4 years ago
defer safeConnClose(c, err)
4 years ago
var user *socks5.User
if ss.user != "" {
user = &socks5.User{
Username: ss.user,
Password: ss.pass,
}
}
_, err = socks5.ClientHandshake(c, metadata.SerializeSocksAddr(), socks5.CmdConnect, user)
4 years ago
return
}
func (ss *Socks5) DialUDP(*M.Metadata) (_ net.PacketConn, err error) {
if ss.unix {
return nil, errors.New("not supported when unix domain socket is enabled")
}
4 years ago
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
defer cancel()
4 years ago
4 years ago
c, err := dialer.DialContext(ctx, "tcp", ss.Addr())
if err != nil {
err = fmt.Errorf("connect to %s: %w", ss.Addr(), err)
return
}
4 years ago
setKeepAlive(c)
4 years ago
defer func() {
4 years ago
if err != nil && c != nil {
4 years ago
c.Close()
}
}()
var user *socks5.User
if ss.user != "" {
user = &socks5.User{
Username: ss.user,
Password: ss.pass,
}
}
// The UDP ASSOCIATE request is used to establish an association within
// the UDP relay process to handle UDP datagrams. The DST.ADDR and
// DST.PORT fields contain the address and port that the client expects
// to use to send UDP datagrams on for the association. The server MAY
// use this information to limit access to the association. If the
// client is not in possession of the information at the time of the UDP
// ASSOCIATE, the client MUST use a port number and address of all
// zeros. RFC1928
var targetAddr socks5.Addr = []byte{socks5.AtypIPv4, 0, 0, 0, 0, 0, 0}
addr, err := socks5.ClientHandshake(c, targetAddr, socks5.CmdUDPAssociate, user)
4 years ago
if err != nil {
return nil, fmt.Errorf("client handshake: %w", err)
4 years ago
}
pc, err := dialer.ListenPacket("udp", "")
if err != nil {
4 years ago
return nil, fmt.Errorf("listen packet: %w", err)
4 years ago
}
go func() {
io.Copy(io.Discard, c)
4 years ago
c.Close()
// A UDP association terminates when the TCP connection that the UDP
// ASSOCIATE request arrived on terminates. RFC1928
pc.Close()
}()
bindAddr := addr.UDPAddr()
4 years ago
if bindAddr.IP.IsUnspecified() { /* e.g. "0.0.0.0" or "::" */
udpAddr, err := net.ResolveUDPAddr("udp", ss.Addr())
if err != nil {
4 years ago
return nil, fmt.Errorf("resolve udp address %s: %w", ss.Addr(), err)
}
bindAddr.IP = udpAddr.IP
}
return &socksPacketConn{PacketConn: pc, rAddr: bindAddr, tcpConn: c}, nil
4 years ago
}
type socksPacketConn struct {
net.PacketConn
rAddr net.Addr
tcpConn net.Conn
}
func (pc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
var packet []byte
if m, ok := addr.(*M.Metadata); ok {
packet, err = socks5.EncodeUDPPacket(m.SerializeSocksAddr(), b)
4 years ago
} else {
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
}
if err != nil {
return
}
return pc.PacketConn.WriteTo(packet, pc.rAddr)
}
func (pc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
4 years ago
n, _, err := pc.PacketConn.ReadFrom(b)
if err != nil {
return 0, nil, err
4 years ago
}
4 years ago
4 years ago
addr, payload, err := socks5.DecodeUDPPacket(b)
if err != nil {
return 0, nil, err
}
udpAddr := addr.UDPAddr()
if udpAddr == nil {
4 years ago
return 0, nil, fmt.Errorf("convert %s to UDPAddr is nil", addr)
4 years ago
}
// due to DecodeUDPPacket is mutable, record addr length
copy(b, payload)
return n - len(addr) - 3, udpAddr, nil
}
func (pc *socksPacketConn) Close() error {
pc.tcpConn.Close()
return pc.PacketConn.Close()
}