From 0226d2cde367956f15253c8aaf26821f2327df8d Mon Sep 17 00:00:00 2001 From: arraykeys Date: Sat, 7 Jul 2018 23:56:56 +0800 Subject: [PATCH] add sps : socks->udp support --- services/socks/socks.go | 277 ---------------------------------- services/socks/udp.go | 315 +++++++++++++++++++++++++++++++++++++++ services/sps/socksudp.go | 302 +++++++++++++++++++++++++++++++++++++ services/sps/sps.go | 43 ++++-- utils/socks/server.go | 64 +++++--- 5 files changed, 691 insertions(+), 310 deletions(-) create mode 100644 services/socks/udp.go create mode 100644 services/sps/socksudp.go diff --git a/services/socks/socks.go b/services/socks/socks.go index 6bae3ec..5020c50 100644 --- a/services/socks/socks.go +++ b/services/socks/socks.go @@ -1,7 +1,6 @@ package socks import ( - "crypto/md5" "crypto/tls" "fmt" "io" @@ -9,14 +8,12 @@ import ( logger "log" "net" "runtime/debug" - "strconv" "strings" "time" "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" - goaes "github.com/snail007/goproxy/utils/aes" "github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/socks" "golang.org/x/crypto/ssh" @@ -359,281 +356,7 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { } } -func (s *Socks) ParentUDPKey() (key []byte) { - switch *s.cfg.ParentType { - case "tcp": - if *s.cfg.ParentKey != "" { - v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) - return []byte(v)[:24] - } - case "tls": - return s.cfg.KeyBytes[:24] - case "kcp": - v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) - return []byte(v)[:24] - } - return -} -func (s *Socks) LocalUDPKey() (key []byte) { - switch *s.cfg.LocalType { - case "tcp": - if *s.cfg.LocalKey != "" { - v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) - return []byte(v)[:24] - } - case "tls": - return s.cfg.KeyBytes[:24] - case "kcp": - v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) - return []byte(v)[:24] - } - return -} -func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { - if *s.cfg.ParentType == "ssh" { - utils.CloseConn(inConn) - return - } - inconnRemoteAddr := (*inConn).RemoteAddr().String() - localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} - udpListener, err := net.ListenUDP("udp", localAddr) - if err != nil { - (*inConn).Close() - udpListener.Close() - s.log.Printf("udp bind fail , %s", err) - return - } - host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) - _, port, _ := net.SplitHostPort(udpListener.LocalAddr().String()) - if len(*s.cfg.LocalIPS) > 0 { - host = (*s.cfg.LocalIPS)[0] - } - s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr) - request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) - s.userConns.Set(inconnRemoteAddr, inConn) - var ( - outUDPConn *net.UDPConn - outconn net.Conn - outconnLocalAddr string - isClosedErr = func(err error) bool { - return err != nil && strings.Contains(err.Error(), "use of closed network connection") - } - destAddr *net.UDPAddr - ) - var clean = func(msg, err string) { - raddr := "" - if outUDPConn != nil { - raddr = outUDPConn.RemoteAddr().String() - outUDPConn.Close() - } - if msg != "" { - if raddr != "" { - s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) - } else { - s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) - } - } - (*inConn).Close() - udpListener.Close() - s.userConns.Remove(inconnRemoteAddr) - if outconn != nil { - outconn.Close() - } - if outconnLocalAddr != "" { - s.userConns.Remove(outconnLocalAddr) - } - } - defer clean("", "") - go func() { - buf := make([]byte, 1) - (*inConn).SetReadDeadline(time.Time{}) - if _, err := (*inConn).Read(buf); err != nil { - clean("udp related tcp conn disconnected with read", err.Error()) - } - }() - go func() { - for { - (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) - if _, err := (*inConn).Write([]byte{0x00}); err != nil { - clean("udp related tcp conn disconnected with write", err.Error()) - return - } - (*inConn).SetWriteDeadline(time.Time{}) - time.Sleep(time.Second * 5) - } - }() - useProxy := true - if *s.cfg.Parent != "" { - dstHost, _, _ := net.SplitHostPort(request.Addr()) - if utils.IsIternalIP(dstHost, *s.cfg.Always) { - useProxy = false - } else { - var isInMap bool - useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) - if !isInMap { - s.checker.Add(request.Addr(), s.Resolve(request.Addr())) - } - } - } else { - useProxy = false - } - if useProxy { - //parent proxy - outconn, err := s.getOutConn(nil, nil, "", false) - if err != nil { - clean("connnect fail", fmt.Sprintf("%s", err)) - return - } - client := socks.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) - if err = client.Handshake(); err != nil { - clean("handshake fail", fmt.Sprintf("%s", err)) - return - } - //outconnRemoteAddr := outconn.RemoteAddr().String() - outconnLocalAddr = outconn.LocalAddr().String() - s.userConns.Set(outconnLocalAddr, &outconn) - go func() { - buf := make([]byte, 1) - outconn.SetReadDeadline(time.Time{}) - if _, err := outconn.Read(buf); err != nil { - clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) - } - }() - //forward to parent udp - //s.log.Printf("parent udp address %s", client.UDPAddr) - destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) - } - s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) - //relay - for { - buf := utils.LeakyBuffer.Get() - defer utils.LeakyBuffer.Put(buf) - n, srcAddr, err := udpListener.ReadFromUDP(buf) - if err != nil { - s.log.Printf("udp listener read fail, %s", err.Error()) - if isClosedErr(err) { - return - } - continue - } - p := socks.NewPacketUDP() - //convert data to raw - if len(s.udpLocalKey) > 0 { - var v []byte - v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) - if err == nil { - err = p.Parse(v) - } - } else { - err = p.Parse(buf[:n]) - } - //err = p.Parse(buf[:n]) - if err != nil { - s.log.Printf("udp listener parse packet fail, %s", err.Error()) - continue - } - port, _ := strconv.Atoi(p.Port()) - - if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { - if destAddr == nil { - destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} - } - outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) - if err != nil { - s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) - continue - } - s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) - go func() { - defer s.udpRelatedPacketConns.Remove(srcAddr.String()) - //out->local io copy - buf := utils.LeakyBuffer.Get() - defer utils.LeakyBuffer.Put(buf) - for { - n, err := outUDPConn.Read(buf) - if err != nil { - s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) - if isClosedErr(err) { - return - } - continue - } - - //var dlen = n - if useProxy { - //forward to local - var v []byte - //convert parent data to raw - if len(s.udpParentKey) > 0 { - v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) - if err != nil { - s.log.Printf("udp outconn parse packet fail, %s", err.Error()) - continue - } - } else { - v = buf[:n] - } - //now v is raw, try convert v to local - if len(s.udpLocalKey) > 0 { - v, _ = goaes.Encrypt(s.udpLocalKey, v) - } - _, err = udpListener.WriteTo(v, srcAddr) - // _, err = udpListener.WriteTo(buf[:n], srcAddr) - } else { - rp := socks.NewPacketUDP() - rp.Build(destAddr.String(), buf[:n]) - v := rp.Bytes() - //dlen = len(v) - //rp.Bytes() v is raw, try convert to local - if len(s.udpLocalKey) > 0 { - v, _ = goaes.Encrypt(s.udpLocalKey, v) - } - _, err = udpListener.WriteTo(v, srcAddr) - } - - if err != nil { - s.udpRelatedPacketConns.Remove(srcAddr.String()) - s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) - if isClosedErr(err) { - return - } - continue - } else { - //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) - } - } - }() - } else { - outUDPConn = v.(*net.UDPConn) - } - //local->out io copy - if useProxy { - //forward to parent - //p is raw, now convert it to parent - var v []byte - if len(s.udpParentKey) > 0 { - v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) - } else { - v = p.Bytes() - } - _, err = outUDPConn.Write(v) - // _, err = outUDPConn.Write(p.Bytes()) - } else { - _, err = outUDPConn.Write(p.Data()) - } - if err != nil { - if isClosedErr(err) { - return - } - s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) - continue - } else { - //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) - } - } - -} func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { var outConn net.Conn var err interface{} diff --git a/services/socks/udp.go b/services/socks/udp.go new file mode 100644 index 0000000..ad66a55 --- /dev/null +++ b/services/socks/udp.go @@ -0,0 +1,315 @@ +package socks + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *Socks) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener, err := net.ListenUDP("udp", localAddr) + if err != nil { + (*inConn).Close() + udpListener.Close() + s.log.Printf("udp bind fail , %s", err) + return + } + host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + _, port, _ := net.SplitHostPort(udpListener.LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + host = (*s.cfg.LocalIPS)[0] + } + s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr) + request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + useProxy := true + if *s.cfg.Parent != "" { + dstHost, _, _ := net.SplitHostPort(request.Addr()) + if utils.IsIternalIP(dstHost, *s.cfg.Always) { + useProxy = false + } else { + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) + if !isInMap { + s.checker.Add(request.Addr(), s.Resolve(request.Addr())) + } + } + } else { + useProxy = false + } + if useProxy { + //parent proxy + outconn, err := s.getOutConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + client := socks.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + } + s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) + //relay + for { + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + //err = p.Parse(buf[:n]) + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, err := outUDPConn.Read(buf) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + //var dlen = n + if useProxy { + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + } else { + rp := socks.NewPacketUDP() + rp.Build(destAddr.String(), buf[:n]) + v := rp.Bytes() + //dlen = len(v) + //rp.Bytes() v is raw, try convert to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + } + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + if useProxy { + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + } else { + _, err = outUDPConn.Write(p.Data()) + } + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/socksudp.go b/services/sps/socksudp.go new file mode 100644 index 0000000..6ab08f8 --- /dev/null +++ b/services/sps/socksudp.go @@ -0,0 +1,302 @@ +package sps + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/conncrypt" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *SPS) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener := serverConn.UDPConnListener + s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + //parent proxy + outconn, err := s.outPool.Get() + //outconn, err := s.GetParentConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + if *s.cfg.ParentCompress { + outconn = utils.NewCompConn(outconn) + } + if *s.cfg.ParentKey != "" { + outconn = conncrypt.New(outconn, &conncrypt.Config{ + Password: *s.cfg.ParentKey, + }) + } + + //client := socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + + s.log.Printf("connect %s for udp", serverConn.Target()) + //socks client + var client *socks.ClientConn + auth := serverConn.AuthData() + if *s.cfg.ParentAuth != "" { + a := strings.Split(*s.cfg.ParentAuth, ":") + if len(a) != 2 { + err = fmt.Errorf("parent auth data format error") + return + } + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil) + } else { + if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil) + } else { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + } + } + + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + //relay + for { + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + //err = p.Parse(buf[:n]) + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, err := outUDPConn.Read(buf) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + + //var dlen = n + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/sps.go b/services/sps/sps.go index e3a7fdf..8060987 100644 --- a/services/sps/sps.go +++ b/services/sps/sps.go @@ -53,22 +53,26 @@ type SPSArgs struct { DisableSocks5 *bool } type SPS struct { - outPool utils.OutConn - cfg SPSArgs - domainResolver utils.DomainResolver - basicAuth utils.BasicAuth - serverChannels []*utils.ServerChannel - userConns utils.ConcurrentMap - log *logger.Logger + outPool utils.OutConn + cfg SPSArgs + domainResolver utils.DomainResolver + basicAuth utils.BasicAuth + serverChannels []*utils.ServerChannel + userConns utils.ConcurrentMap + log *logger.Logger + udpRelatedPacketConns utils.ConcurrentMap + udpLocalKey []byte + udpParentKey []byte } func NewSPS() services.Service { return &SPS{ - outPool: utils.OutConn{}, - cfg: SPSArgs{}, - basicAuth: utils.BasicAuth{}, - serverChannels: []*utils.ServerChannel{}, - userConns: utils.NewConcurrentMap(), + outPool: utils.OutConn{}, + cfg: SPSArgs{}, + basicAuth: utils.BasicAuth{}, + serverChannels: []*utils.ServerChannel{}, + userConns: utils.NewConcurrentMap(), + udpRelatedPacketConns: utils.NewConcurrentMap(), } } func (s *SPS) CheckArgs() (err error) { @@ -93,6 +97,8 @@ func (s *SPS) CheckArgs() (err error) { } } } + s.udpLocalKey = s.LocalUDPKey() + s.udpParentKey = s.ParentUDPKey() return } func (s *SPS) InitService() (err error) { @@ -210,6 +216,11 @@ func (s *SPS) callback(inConn net.Conn) { } } func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { + enableUDP := *s.cfg.ParentServiceType == "socks" + udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + udpIP = (*s.cfg.LocalIPS)[0] + } bInConn := utils.NewBufferedConn(*inConn) //important //action read will regist read event to system, @@ -243,15 +254,19 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { //socks5 server var serverConn *socks.ServerConn if s.IsBasicAuth() { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, enableUDP, udpIP, nil) } else { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, enableUDP, udpIP, nil) } if err = serverConn.Handshake(); err != nil { return } address = serverConn.Target() auth = serverConn.AuthData() + if serverConn.IsUDP() { + s.proxyUDP(inConn, serverConn) + return + } } else if utils.IsHTTP(h) || isSNI != "" { if *s.cfg.DisableHTTP { (*inConn).Close() diff --git a/utils/socks/server.go b/utils/socks/server.go index 67f26bf..8808562 100644 --- a/utils/socks/server.go +++ b/utils/socks/server.go @@ -2,10 +2,11 @@ package socks import ( "fmt" - "github.com/snail007/goproxy/utils" "net" "strings" "time" + + "github.com/snail007/goproxy/utils" ) const ( @@ -54,26 +55,27 @@ type ServerConn struct { methods []uint8 method uint8 //request - cmd uint8 - reserve uint8 - addressType uint8 - dstAddr string - dstPort string - dstHost string - udpAddress string + cmd uint8 + reserve uint8 + addressType uint8 + dstAddr string + dstPort string + dstHost string + UDPConnListener *net.UDPConn + enableUDP bool + udpIP string } -func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, udpAddress string, header []byte) *ServerConn { - if udpAddress == "" { - udpAddress = "0.0.0.0:16666" - } +func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, enableUDP bool, udpHost string, header []byte) *ServerConn { + s := &ServerConn{ - conn: conn, - timeout: timeout, - auth: auth, - header: header, - ver: VERSION_V5, - udpAddress: udpAddress, + conn: conn, + timeout: timeout, + auth: auth, + header: header, + ver: VERSION_V5, + enableUDP: enableUDP, + udpIP: udpHost, } return s @@ -84,6 +86,12 @@ func (s *ServerConn) Close() { func (s *ServerConn) AuthData() Auth { return Auth{s.user, s.password} } +func (s *ServerConn) IsUDP() bool { + return s.cmd == CMD_ASSOCIATE +} +func (s *ServerConn) IsTCP() bool { + return s.cmd == CMD_CONNECT +} func (s *ServerConn) Method() uint8 { return s.method } @@ -205,11 +213,29 @@ func (s *ServerConn) Handshake() (err error) { return } case CMD_ASSOCIATE: - err = request.UDPReply(REP_SUCCESS, s.udpAddress) + if !s.enableUDP { + request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") + if err != nil { + err = fmt.Errorf("UDPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err) + return + } + err = fmt.Errorf("cmd associate not supported, form: %s", remoteAddr) + return + } + a, _ := net.ResolveUDPAddr("udp", ":0") + s.UDPConnListener, err = net.ListenUDP("udp", a) + if err != nil { + request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") + err = fmt.Errorf("udp bind fail,ERR: %s , for %s", err, remoteAddr) + return + } + _, port, _ := net.SplitHostPort(s.UDPConnListener.LocalAddr().String()) + err = request.UDPReply(REP_SUCCESS, net.JoinHostPort(s.udpIP, port)) if err != nil { err = fmt.Errorf("UDPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) return } + } //fill socks info