diff --git a/dialer.go b/dialer.go index 4b8b66c..e3d7d91 100644 --- a/dialer.go +++ b/dialer.go @@ -14,6 +14,9 @@ type Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) + // DialUDP returns a UDP PacketConn + DialUDP(network, addr string) (c net.PacketConn, err error) + // Get the dialer by dstAddr NextDialer(dstAddr string) Dialer } diff --git a/direct.go b/direct.go index fa1e74e..d8f7775 100644 --- a/direct.go +++ b/direct.go @@ -12,6 +12,9 @@ var Direct = &direct{} func (d *direct) Addr() string { return "DIRECT" } func (d *direct) Dial(network, addr string) (net.Conn, error) { + if network == "uot" { + network = "udp" + } c, err := net.Dial(network, addr) if c, ok := c.(*net.TCPConn); ok { c.SetKeepAlive(true) @@ -19,4 +22,14 @@ func (d *direct) Dial(network, addr string) (net.Conn, error) { return c, err } +func (d *direct) DialUDP(network, addr string) (net.PacketConn, error) { + uAddr, err := net.ResolveUDPAddr(network, addr) + if err != nil { + logf("ResolveUDPAddr error: %s", err) + return nil, err + } + + return net.DialUDP("udp", nil, uAddr) +} + func (d *direct) NextDialer(dstAddr string) Dialer { return d } diff --git a/http.go b/http.go index 523fd33..54e984a 100644 --- a/http.go +++ b/http.go @@ -214,6 +214,11 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) { return nil, errors.New("cound not connect remote address: " + addr + ". error code: " + code) } +// DialUDP . +func (s *HTTP) DialUDP(network, addr string) (net.PacketConn, error) { + return nil, errors.New("udp not supported by http proxy now") +} + // parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { line, err := tp.ReadLine() diff --git a/main.go b/main.go index 8a9193c..b741cfa 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( ) // VERSION . -const VERSION = "0.4.3" +const VERSION = "0.5.0" func dialerFromConf() Dialer { // global forwarders in xx.conf diff --git a/rule.go b/rule.go index 5a38473..ad9d186 100644 --- a/rule.go +++ b/rule.go @@ -56,7 +56,8 @@ func NewRuleDialer(rules []*RuleConf, gDialer Dialer) *RuleDialer { } // Addr returns RuleDialer's address, always be "RULES" -func (rd *RuleDialer) Addr() string { return "RULES" } +// func (rd *RuleDialer) Addr() string { return "RULES" } +func (rd *RuleDialer) Addr() string { return rd.gDialer.Addr() } // NextDialer return next dialer according to rule func (rd *RuleDialer) NextDialer(dstAddr string) Dialer { @@ -112,6 +113,11 @@ func (rd *RuleDialer) Dial(network, addr string) (net.Conn, error) { return rd.NextDialer(addr).Dial(network, addr) } +// DialUDP . +func (rd *RuleDialer) DialUDP(network, addr string) (net.PacketConn, error) { + return rd.NextDialer(addr).DialUDP(network, addr) +} + // AddDomainIP used to update ipMap rules according to domainMap rule func (rd *RuleDialer) AddDomainIP(domain, ip string) error { if ip != "" { diff --git a/server.go b/server.go index 426af03..248b622 100644 --- a/server.go +++ b/server.go @@ -50,6 +50,9 @@ func ServerFromURL(s string, sDialer Dialer) (Server, error) { case "tcptun": d := strings.Split(addr, "=") return NewTCPTun(d[0], d[1], sDialer) + case "udptun": + d := strings.Split(addr, "=") + return NewUDPTun(d[0], d[1], sDialer) case "dnstun": d := strings.Split(addr, "=") return NewDNSTun(d[0], d[1], sDialer) diff --git a/socks5.go b/socks5.go index 3e686fd..bad9425 100644 --- a/socks5.go +++ b/socks5.go @@ -156,6 +156,11 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { return c, nil } +// DialUDP . +func (s *SOCKS5) DialUDP(network, addr string) (net.PacketConn, error) { + return nil, errors.New("udp not supported by socks5 proxy now") +} + // connect takes an existing connection to a socks5 proxy server, // and commands the server to extend that connection to target, // which must be a canonical address with a host and port. diff --git a/ss.go b/ss.go index fba69f7..2647d84 100644 --- a/ss.go +++ b/ss.go @@ -214,11 +214,22 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) { return nil, errors.New("Unable to parse address: " + addr) } - // udp over tcp tag - if network == "udp" { + switch network { + case "tcp": + return s.DialTCP(target) + case "uot": target[0] = target[0] | 0x8 + return s.DialTCP(target) + // case "udp": + // return s.DialUDP(target) + default: + return nil, errors.New("Unknown schema: " + network) } +} + +// DialTCP connects to the address addr via the proxy. +func (s *SS) DialTCP(target Addr) (net.Conn, error) { c, err := s.cDialer.Dial("tcp", s.addr) if err != nil { logf("dial to %s error: %s", s.addr, err) @@ -238,6 +249,19 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) { return c, err } +// DialUDP . +func (s *SS) DialUDP(network, addr string) (net.PacketConn, error) { + target := ParseAddr(addr) + if target == nil { + return nil, errors.New("Unable to parse address: " + addr) + } + + c, _ := net.ListenPacket(network, "") + c = s.PacketConn(c) + + return c, nil +} + // ListCipher . func ListCipher() string { return strings.Join(core.ListCipher(), " ") diff --git a/strategy.go b/strategy.go index 98307e3..6c2efdc 100644 --- a/strategy.go +++ b/strategy.go @@ -5,8 +5,8 @@ import ( "io" "net" "strings" - "time" "sync" + "time" ) // NewStrategyDialer returns a new Strategy Dialer @@ -53,7 +53,7 @@ func newRRDialer(dialers []Dialer, website string, duration int) *rrDialer { rr.duration = duration for k := range dialers { - rr.status.Store(k,true) + rr.status.Store(k, true) go rr.checkDialer(k) } @@ -65,6 +65,10 @@ func (rr *rrDialer) Dial(network, addr string) (net.Conn, error) { return rr.NextDialer(addr).Dial(network, addr) } +func (rr *rrDialer) DialUDP(network, addr string) (net.PacketConn, error) { + return rr.NextDialer(addr).DialUDP(network, addr) +} + func (rr *rrDialer) NextDialer(dstAddr string) Dialer { n := len(rr.dialers) if n == 1 { @@ -75,7 +79,7 @@ func (rr *rrDialer) NextDialer(dstAddr string) Dialer { for i := 0; i < n; i++ { rr.idx = (rr.idx + 1) % n result, ok := rr.status.Load(rr.idx) - if (ok && result.(bool)) { + if ok && result.(bool) { found = true break } @@ -151,7 +155,7 @@ func (ha *haDialer) Dial(network, addr string) (net.Conn, error) { result, ok := ha.status.Load(ha.idx) - if (ok && !result.(bool)) { + if ok && !result.(bool) { d = ha.NextDialer(addr) } diff --git a/udptun.go b/udptun.go new file mode 100644 index 0000000..6a766d1 --- /dev/null +++ b/udptun.go @@ -0,0 +1,74 @@ +package main + +import ( + "net" + "time" +) + +// UDPTun struct +type UDPTun struct { + *Forwarder + sDialer Dialer + + raddr string +} + +// NewUDPTun returns a UDPTun proxy. +func NewUDPTun(addr, raddr string, sDialer Dialer) (*UDPTun, error) { + s := &UDPTun{ + Forwarder: NewForwarder(addr, nil), + sDialer: sDialer, + raddr: raddr, + } + + return s, nil +} + +// ListenAndServe . +func (s *UDPTun) ListenAndServe() { + c, err := net.ListenPacket("udp", s.addr) + if err != nil { + logf("proxy-udptun failed to listen on %s: %v", s.addr, err) + return + } + defer c.Close() + + logf("proxy-udptun listening UDP on %s", s.addr) + + // var nm sync.Map + buf := make([]byte, udpBufSize) + tgt := ParseAddr(s.raddr) + copy(buf, tgt) + + for { + n, clientAddr, err := c.ReadFrom(buf[len(tgt):]) + if err != nil { + logf("proxy-udptun read error: %v", err) + continue + } + + go func() { + rc, err := s.sDialer.DialUDP("udp", s.raddr) + if err != nil { + logf("proxy-udptun failed to connect to server %v: %v", s.raddr, err) + return + } + + // TODO: check here, get the correct sDialer's addr + sUDPAddr, err := net.ResolveUDPAddr("udp", s.sDialer.Addr()) + if err != nil { + logf("proxy-udptun failed to ResolveUDPAddr %", s.sDialer.Addr()) + return + } + + rc.WriteTo(buf[:len(tgt)+n], sUDPAddr) + + go func() { + timedCopy(c, clientAddr, rc, 5*time.Minute, false) + rc.Close() + }() + + logf("proxy-udptun %s <-> %s", clientAddr, s.raddr) + }() + } +} diff --git a/uottun.go b/uottun.go index 97abd28..fce3874 100644 --- a/uottun.go +++ b/uottun.go @@ -46,21 +46,22 @@ func (s *UoTTun) ListenAndServe() { } go func() { - // NOTE: acturally udp over tcp - rc, err := s.sDialer.Dial("udp", s.raddr) + rc, err := s.sDialer.Dial("uot", s.raddr) if err != nil { - logf("failed to connect to server %v: %v", s.raddr, err) + logf("proxy-uottun failed to connect to server %v: %v", s.raddr, err) return } rc.Write(buf[:n]) - // no remote forwarder + // no remote forwarder, just a local udp forwarder if urc, ok := rc.(*net.UDPConn); ok { + go func() { timedCopy(c, clientAddr, urc, 5*time.Minute, false) urc.Close() }() + } else { // remote forwarder, udp over tcp resp, err := ioutil.ReadAll(rc) if err != nil {