Files
tun2socks/proxy/utils.go
2019-08-14 21:16:55 +08:00

165 lines
3.3 KiB
Go

package proxy
import (
"errors"
"fmt"
"github.com/xjasonlyu/tun2socks/common/log"
"io"
"io/ioutil"
"net"
"time"
"github.com/xjasonlyu/tun2socks/common/dns"
"github.com/xjasonlyu/tun2socks/proxy/socks"
)
// DNS lookup
func lookupHost(fakeDNS dns.FakeDNS, target net.Addr) (targetHost string, err error) {
var targetIP net.IP
switch addr := target.(type) {
case *net.TCPAddr:
targetIP = addr.IP
case *net.UDPAddr:
targetIP = addr.IP
default:
err = errors.New("invalid target type")
return
}
targetHost = targetIP.String()
// Replace with a domain name if target address IP is a fake IP
if fakeDNS != nil {
log.Warnf("lookup dns: %v", targetHost)
if host, exist := fakeDNS.IPToHost(targetIP); exist {
targetHost = host
}
}
return
}
// TCP functions
type duplexConn interface {
net.Conn
CloseRead() error
CloseWrite() error
}
func tcpCloseRead(conn net.Conn) {
if c, ok := conn.(duplexConn); ok {
c.CloseRead()
}
}
func tcpCloseWrite(conn net.Conn) {
if c, ok := conn.(duplexConn); ok {
c.CloseWrite()
}
}
func tcpKeepAlive(conn net.Conn) {
if tcp, ok := conn.(*net.TCPConn); ok {
tcp.SetKeepAlive(true)
tcp.SetKeepAlivePeriod(30 * time.Second)
}
}
// Socks dialer
func dial(proxy, target string) (net.Conn, error) {
c, err := net.DialTimeout("tcp", proxy, 30*time.Second)
if err != nil {
return nil, fmt.Errorf("%s connect error", proxy)
}
targetAddr := socks.ParseAddr(target)
if targetAddr == nil {
return nil, fmt.Errorf("target address parse error")
}
if _, err := socks.ClientHandshake(c, targetAddr, socks.CmdConnect); err != nil {
return nil, err
}
return c, nil
}
func dialUDP(proxy, target string) (_ net.PacketConn, _ net.Addr, err error) {
c, err := net.DialTimeout("tcp", proxy, 30*time.Second)
if err != nil {
err = fmt.Errorf("%s connect error", proxy)
return
}
// tcp set keepalive
tcpKeepAlive(c)
defer func() {
if err != nil {
c.Close()
}
}()
targetAddr := socks.ParseAddr(target)
if targetAddr == nil {
err = fmt.Errorf("target address parse error")
return
}
bindAddr, err := socks.ClientHandshake(c, targetAddr, socks.CmdUDPAssociate)
if err != nil {
err = fmt.Errorf("%v client hanshake error", err)
return
}
addr, err := net.ResolveUDPAddr("udp", bindAddr.String())
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, tcpConn: c, targetAddr: targetAddr}, addr, nil
}
// Socks wrapped UDPConn
type socksUDPConn struct {
net.PacketConn
tcpConn net.Conn
targetAddr socks.Addr
}
func (c *socksUDPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
packet, err := socks.EncodeUDPPacket(c.targetAddr, b)
if err != nil {
return
}
return c.PacketConn.WriteTo(packet, addr)
}
func (c *socksUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, a, e := c.PacketConn.ReadFrom(b)
if e != nil {
return 0, nil, e
}
addr, payload, err := socks.DecodeUDPPacket(b)
if err != nil {
return 0, nil, err
}
copy(b, payload)
return n - len(addr) - 3, a, nil
}
func (c *socksUDPConn) Close() error {
c.tcpConn.Close()
return c.PacketConn.Close()
}