add go-tun2socks code

This commit is contained in:
Jason
2019-07-16 11:37:52 +08:00
parent 828ba9948d
commit 6d01dec5a4
301 changed files with 69694 additions and 1 deletions
+162
View File
@@ -0,0 +1,162 @@
// Code in this file are grabbed from https://github.com/nadoo/glider, which
// is also referencing another repo: https://github.com/shadowsocks/go-shadowsocks2
package socks
import (
"errors"
"io"
"net"
"strconv"
)
// SOCKS request commands as defined in RFC 1928 section 4.
const (
socks5Connect = 1
socks5Bind = 2
socks5UDPAssociate = 3
)
// SOCKS address types as defined in RFC 1928 section 5.
const (
socks5IP4 = 1
socks5Domain = 3
socks5IP6 = 4
)
var socks5Errors = []error{
errors.New(""),
errors.New("general failure"),
errors.New("connection forbidden"),
errors.New("network unreachable"),
errors.New("host unreachable"),
errors.New("connection refused"),
errors.New("TTL expired"),
errors.New("command not supported"),
errors.New("address type not supported"),
errors.New("socks5UDPAssociate"),
}
// MaxAddrLen is the maximum size of SOCKS address in bytes.
const MaxAddrLen = 1 + 1 + 255 + 2
// ATYP return the address type
func ATYP(b byte) int {
return int(b &^ 0x8)
}
// Addr represents a SOCKS address as defined in RFC 1928 section 5.
type Addr []byte
// String serializes SOCKS address a to string form.
func (a Addr) String() string {
var host, port string
switch ATYP(a[0]) { // address type
case socks5Domain:
host = string(a[2 : 2+int(a[1])])
port = strconv.Itoa((int(a[2+int(a[1])]) << 8) | int(a[2+int(a[1])+1]))
case socks5IP4:
host = net.IP(a[1 : 1+net.IPv4len]).String()
port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1]))
case socks5IP6:
host = net.IP(a[1 : 1+net.IPv6len]).String()
port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1]))
}
return net.JoinHostPort(host, port)
}
// ParseAddr parses the address in string s. Returns nil if failed.
func ParseAddr(s string) Addr {
var addr Addr
host, port, err := net.SplitHostPort(s)
if err != nil {
return nil
}
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
addr = make([]byte, 1+net.IPv4len+2)
addr[0] = socks5IP4
copy(addr[1:], ip4)
} else {
addr = make([]byte, 1+net.IPv6len+2)
addr[0] = socks5IP6
copy(addr[1:], ip)
}
} else {
if len(host) > 255 {
return nil
}
addr = make([]byte, 1+1+len(host)+2)
addr[0] = socks5Domain
addr[1] = byte(len(host))
copy(addr[2:], host)
}
portnum, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return nil
}
addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum)
return addr
}
func readAddr(r io.Reader, b []byte) (Addr, error) {
if len(b) < MaxAddrLen {
return nil, io.ErrShortBuffer
}
_, err := io.ReadFull(r, b[:1]) // read 1st byte for address type
if err != nil {
return nil, err
}
switch ATYP(b[0]) {
case socks5Domain:
_, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length
if err != nil {
return nil, err
}
_, err = io.ReadFull(r, b[2:2+int(b[1])+2])
return b[:1+1+int(b[1])+2], err
case socks5IP4:
_, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
return b[:1+net.IPv4len+2], err
case socks5IP6:
_, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
return b[:1+net.IPv6len+2], err
}
return nil, socks5Errors[8]
}
// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed.
func SplitAddr(b []byte) Addr {
addrLen := 1
if len(b) < addrLen {
return nil
}
switch ATYP(b[0]) {
case socks5Domain:
if len(b) < 2 {
return nil
}
addrLen = 1 + 1 + int(b[1]) + 2
case socks5IP4:
addrLen = 1 + net.IPv4len + 2
case socks5IP6:
addrLen = 1 + net.IPv6len + 2
default:
return nil
}
if len(b) < addrLen {
return nil
}
return b[:addrLen]
}
+192
View File
@@ -0,0 +1,192 @@
package socks
import (
"io"
"net"
"strconv"
"sync"
"time"
"golang.org/x/net/proxy"
"github.com/xjasonlyu/tun2socks/common/dns"
"github.com/xjasonlyu/tun2socks/common/log"
"github.com/xjasonlyu/tun2socks/common/lsof"
"github.com/xjasonlyu/tun2socks/common/stats"
"github.com/xjasonlyu/tun2socks/core"
)
type tcpHandler struct {
sync.Mutex
proxyHost string
proxyPort uint16
fakeDns dns.FakeDns
sessionStater stats.SessionStater
}
func NewTCPHandler(proxyHost string, proxyPort uint16, fakeDns dns.FakeDns, sessionStater stats.SessionStater) core.TCPConnHandler {
return &tcpHandler{
proxyHost: proxyHost,
proxyPort: proxyPort,
fakeDns: fakeDns,
sessionStater: sessionStater,
}
}
type direction byte
const (
dirUplink direction = iota
dirDownlink
)
func statsCopy(dst io.Writer, src io.Reader, sess *stats.Session, dir direction) (written int64, err error) {
buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
switch dir {
case dirUplink:
sess.AddUploadBytes(int64(nw))
case dirDownlink:
sess.AddDownloadBytes(int64(nw))
default:
}
written += int64(nw)
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}
type duplexConn interface {
net.Conn
CloseRead() error
CloseWrite() error
}
func (h *tcpHandler) relay(lhs, rhs net.Conn, sess *stats.Session) {
var err error
upCh := make(chan struct{})
cls := func(dir direction, interrupt bool) {
lhsDConn, lhsOk := lhs.(duplexConn)
rhsDConn, rhsOk := rhs.(duplexConn)
if !interrupt && lhsOk && rhsOk {
switch dir {
case dirUplink:
_ = lhsDConn.CloseRead()
_ = rhsDConn.CloseWrite()
case dirDownlink:
_ = lhsDConn.CloseWrite()
_ = rhsDConn.CloseRead()
default:
panic("unexpected direction")
}
} else {
_ = lhs.Close()
_ = rhs.Close()
}
}
// Uplink
go func() {
if h.sessionStater != nil && sess != nil {
_, err = statsCopy(rhs, lhs, sess, dirUplink)
} else {
_, err = io.Copy(rhs, lhs)
}
if err != nil {
cls(dirUplink, true) // interrupt the conn if the error is not nil (not EOF)
} else {
cls(dirUplink, false) // half close uplink direction of the TCP conn if possible
}
upCh <- struct{}{}
}()
// Downlink
if h.sessionStater != nil && sess != nil {
_, err = statsCopy(lhs, rhs, sess, dirDownlink)
} else {
_, err = io.Copy(lhs, rhs)
}
if err != nil {
cls(dirDownlink, true)
} else {
cls(dirDownlink, false)
}
<-upCh // Wait for uplink done.
if h.sessionStater != nil {
h.sessionStater.RemoveSession(lhs)
}
}
func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
dialer, err := proxy.SOCKS5("tcp", core.ParseTCPAddr(h.proxyHost, h.proxyPort).String(), nil, nil)
if err != nil {
return err
}
// Replace with a domain name if target address IP is a fake IP.
var targetHost string
if h.fakeDns != nil && h.fakeDns.IsFakeIP(target.IP) {
targetHost = h.fakeDns.IPToHost(target.IP)
} else {
targetHost = target.IP.String()
}
dest := net.JoinHostPort(targetHost, strconv.Itoa(target.Port))
c, err := dialer.Dial(target.Network(), dest)
if err != nil {
return err
}
var process string
var sess *stats.Session
if h.sessionStater != nil {
// Get name of the process.
localHost, localPortStr, _ := net.SplitHostPort(conn.LocalAddr().String())
localPortInt, _ := strconv.Atoi(localPortStr)
process, err = lsof.GetCommandNameBySocket(target.Network(), localHost, uint16(localPortInt))
if err != nil {
process = "unknown process"
}
sess = &stats.Session{
ProcessName: process,
Network: target.Network(),
LocalAddr: conn.LocalAddr().String(),
RemoteAddr: dest,
UploadBytes: 0,
DownloadBytes: 0,
SessionStart: time.Now(),
}
h.sessionStater.AddSession(conn, sess)
}
go h.relay(conn, c, sess)
log.Access(process, "proxy", target.Network(), conn.LocalAddr().String(), dest)
return nil
}
+271
View File
@@ -0,0 +1,271 @@
package socks
import (
"errors"
"fmt"
"io"
"net"
"strconv"
"sync"
"time"
"github.com/xjasonlyu/tun2socks/common/dns"
"github.com/xjasonlyu/tun2socks/common/log"
"github.com/xjasonlyu/tun2socks/common/lsof"
"github.com/xjasonlyu/tun2socks/common/stats"
"github.com/xjasonlyu/tun2socks/core"
)
type udpHandler struct {
sync.Mutex
proxyHost string
proxyPort uint16
udpConns map[core.UDPConn]net.PacketConn
tcpConns map[core.UDPConn]net.Conn
remoteAddrs map[core.UDPConn]*net.UDPAddr // UDP relay server addresses
timeout time.Duration
dnsCache dns.DnsCache
fakeDns dns.FakeDns
sessionStater stats.SessionStater
}
func NewUDPHandler(proxyHost string, proxyPort uint16, timeout time.Duration, dnsCache dns.DnsCache, fakeDns dns.FakeDns, sessionStater stats.SessionStater) core.UDPConnHandler {
return &udpHandler{
proxyHost: proxyHost,
proxyPort: proxyPort,
udpConns: make(map[core.UDPConn]net.PacketConn, 8),
tcpConns: make(map[core.UDPConn]net.Conn, 8),
remoteAddrs: make(map[core.UDPConn]*net.UDPAddr, 8),
dnsCache: dnsCache,
fakeDns: fakeDns,
timeout: timeout,
sessionStater: sessionStater,
}
}
func (h *udpHandler) handleTCP(conn core.UDPConn, c net.Conn) {
buf := core.NewBytes(core.BufSize)
defer core.FreeBytes(buf)
for {
_ = c.SetDeadline(time.Time{})
_, err := c.Read(buf)
if err == io.EOF {
log.Warnf("UDP associate to %v closed by remote", c.RemoteAddr())
h.Close(conn)
return
} else if err != nil {
h.Close(conn)
return
}
}
}
func (h *udpHandler) fetchUDPInput(conn core.UDPConn, input net.PacketConn) {
buf := core.NewBytes(core.BufSize)
defer func() {
h.Close(conn)
core.FreeBytes(buf)
}()
for {
_ = input.SetDeadline(time.Now().Add(h.timeout))
n, _, err := input.ReadFrom(buf)
if err != nil {
// log.Printf("read remote failed: %v", err)
return
}
addr := SplitAddr(buf[3:])
resolvedAddr, err := net.ResolveUDPAddr("udp", addr.String())
if err != nil {
return
}
n, err = conn.WriteFrom(buf[int(3+len(addr)):n], resolvedAddr)
if n > 0 && h.sessionStater != nil {
if sess := h.sessionStater.GetSession(conn); sess != nil {
sess.AddDownloadBytes(int64(n))
}
}
if err != nil {
log.Warnf("write local failed: %v", err)
return
}
if h.dnsCache != nil {
_, port, err := net.SplitHostPort(addr.String())
if err != nil {
panic("impossible error")
}
if port == strconv.Itoa(dns.CommonDnsPort) {
h.dnsCache.Store(buf[int(3+len(addr)):n])
return // DNS response
}
}
}
}
func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error {
if target == nil {
return h.connectInternal(conn, "")
}
// Replace with a domain name if target address IP is a fake IP.
targetHost := target.IP.String()
if h.fakeDns != nil {
if target.Port == dns.CommonDnsPort {
return nil // skip dns
}
if h.fakeDns.IsFakeIP(target.IP) {
targetHost = h.fakeDns.IPToHost(target.IP)
}
}
dest := net.JoinHostPort(targetHost, strconv.Itoa(target.Port))
return h.connectInternal(conn, dest)
}
func (h *udpHandler) connectInternal(conn core.UDPConn, dest string) error {
c, err := net.DialTimeout("tcp", core.ParseTCPAddr(h.proxyHost, h.proxyPort).String(), 4*time.Second)
if err != nil {
return err
}
_ = c.SetDeadline(time.Now().Add(4 * time.Second))
// send VER, NMETHODS, METHODS
_, _ = c.Write([]byte{5, 1, 0})
buf := make([]byte, MaxAddrLen)
// read VER METHOD
if _, err := io.ReadFull(c, buf[:2]); err != nil {
return err
}
if len(dest) != 0 {
targetAddr := ParseAddr(dest)
// write VER CMD RSV ATYP DST.ADDR DST.PORT
_, _ = c.Write(append([]byte{5, socks5UDPAssociate, 0}, targetAddr...))
} else {
_, _ = c.Write(append([]byte{5, socks5UDPAssociate, 0}, []byte{1, 0, 0, 0, 0, 0, 0}...))
}
// read VER REP RSV ATYP BND.ADDR BND.PORT
if _, err := io.ReadFull(c, buf[:3]); err != nil {
return err
}
rep := buf[1]
if rep != 0 {
return errors.New("SOCKS handshake failed")
}
remoteAddr, err := readAddr(c, buf)
if err != nil {
return err
}
resolvedRemoteAddr, err := net.ResolveUDPAddr("udp", remoteAddr.String())
if err != nil {
return errors.New("failed to resolve remote address")
}
go h.handleTCP(conn, c)
pc, err := net.ListenPacket("udp", "")
if err != nil {
return err
}
h.Lock()
h.tcpConns[conn] = c
h.udpConns[conn] = pc
h.remoteAddrs[conn] = resolvedRemoteAddr
h.Unlock()
go h.fetchUDPInput(conn, pc)
if len(dest) != 0 {
var process string
if h.sessionStater != nil {
// Get name of the process.
localHost, localPortStr, _ := net.SplitHostPort(conn.LocalAddr().String())
localPortInt, _ := strconv.Atoi(localPortStr)
process, err = lsof.GetCommandNameBySocket(conn.LocalAddr().Network(), localHost, uint16(localPortInt))
if err != nil {
process = "unknown process"
}
sess := &stats.Session{
ProcessName: process,
Network: conn.LocalAddr().Network(),
LocalAddr: conn.LocalAddr().String(),
RemoteAddr: dest,
UploadBytes: 0,
DownloadBytes: 0,
SessionStart: time.Now(),
}
h.sessionStater.AddSession(conn, sess)
}
log.Access(process, "proxy", "udp", conn.LocalAddr().String(), dest)
}
return nil
}
func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error {
h.Lock()
pc, ok1 := h.udpConns[conn]
remoteAddr, ok2 := h.remoteAddrs[conn]
h.Unlock()
// use system DNS instead of force override
if ok1 && ok2 {
var targetHost string
if h.fakeDns != nil && h.fakeDns.IsFakeIP(addr.IP) {
targetHost = h.fakeDns.IPToHost(addr.IP)
} else {
targetHost = addr.IP.String()
}
dest := net.JoinHostPort(targetHost, strconv.Itoa(addr.Port))
buf := append([]byte{0, 0, 0}, ParseAddr(dest)...)
buf = append(buf, data[:]...)
n, err := pc.WriteTo(buf, remoteAddr)
if n > 0 && h.sessionStater != nil {
if sess := h.sessionStater.GetSession(conn); sess != nil {
sess.AddUploadBytes(int64(n))
}
}
if err != nil {
h.Close(conn)
return errors.New(fmt.Sprintf("write remote failed: %v", err))
}
return nil
} else {
h.Close(conn)
return errors.New(fmt.Sprintf("proxy connection %v->%v does not exists", conn.LocalAddr(), addr))
}
}
func (h *udpHandler) Close(conn core.UDPConn) {
_ = conn.Close()
h.Lock()
defer h.Unlock()
if c, ok := h.tcpConns[conn]; ok {
_ = c.Close()
delete(h.tcpConns, conn)
}
if pc, ok := h.udpConns[conn]; ok {
_ = pc.Close()
delete(h.udpConns, conn)
}
delete(h.remoteAddrs, conn)
if h.sessionStater != nil {
h.sessionStater.RemoveSession(conn)
}
}