github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/services/sps/ssudp.go (about)

     1  package sps
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net"
     7  	"runtime/debug"
     8  	"time"
     9  
    10  	"github.com/AntonOrnatskyi/goproxy/utils"
    11  	goaes "github.com/AntonOrnatskyi/goproxy/utils/aes"
    12  	"github.com/AntonOrnatskyi/goproxy/utils/socks"
    13  )
    14  
    15  func (s *SPS) RunSSUDP(addr string) (err error) {
    16  	a, _ := net.ResolveUDPAddr("udp", addr)
    17  	listener, err := net.ListenUDP("udp", a)
    18  	if err != nil {
    19  		s.log.Printf("ss udp bind error %s", err)
    20  		return
    21  	}
    22  	s.log.Printf("ss udp on %s", listener.LocalAddr())
    23  	s.udpRelatedPacketConns.Set(addr, listener)
    24  	go func() {
    25  		defer func() {
    26  			if e := recover(); e != nil {
    27  				s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack()))
    28  			}
    29  		}()
    30  		buf := utils.LeakyBuffer.Get()
    31  		defer utils.LeakyBuffer.Put(buf)
    32  		for {
    33  			n, srcAddr, err := listener.ReadFrom(buf)
    34  			if err != nil {
    35  				s.log.Printf("read from client error %s", err)
    36  				if utils.IsNetClosedErr(err) {
    37  					return
    38  				}
    39  				continue
    40  			}
    41  			var (
    42  				inconnRemoteAddr = srcAddr.String()
    43  				outUDPConn       *net.UDPConn
    44  				outconn          net.Conn
    45  				outconnLocalAddr string
    46  				destAddr         *net.UDPAddr
    47  				clean            = func(msg, err string) {
    48  					raddr := ""
    49  					if outUDPConn != nil {
    50  						raddr = outUDPConn.RemoteAddr().String()
    51  						outUDPConn.Close()
    52  					}
    53  					if msg != "" {
    54  						if raddr != "" {
    55  							s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr)
    56  						} else {
    57  							s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr)
    58  						}
    59  					}
    60  					s.userConns.Remove(inconnRemoteAddr)
    61  					if outconn != nil {
    62  						outconn.Close()
    63  					}
    64  					if outconnLocalAddr != "" {
    65  						s.userConns.Remove(outconnLocalAddr)
    66  					}
    67  				}
    68  			)
    69  			defer clean("", "")
    70  
    71  			raw := new(bytes.Buffer)
    72  			raw.Write([]byte{0x00, 0x00, 0x00})
    73  			raw.Write(s.localCipher.Decrypt(buf[:n]))
    74  			socksPacket := socks.NewPacketUDP()
    75  			err = socksPacket.Parse(raw.Bytes())
    76  			raw = nil
    77  			if err != nil {
    78  				s.log.Printf("udp parse error %s", err)
    79  				return
    80  			}
    81  
    82  			if v, ok := s.udpRelatedPacketConns.Get(inconnRemoteAddr); !ok {
    83  				//socks client
    84  				lbAddr := s.lb.Select(inconnRemoteAddr, *s.cfg.LoadBalanceOnlyHA)
    85  				outconn, err := s.GetParentConn(lbAddr)
    86  				if err != nil {
    87  					clean("connnect fail", fmt.Sprintf("%s", err))
    88  					return
    89  				}
    90  
    91  				client, err := s.HandshakeSocksParent(s.getParentAuth(lbAddr), &outconn, "udp", socksPacket.Addr(), socks.Auth{}, true)
    92  				if err != nil {
    93  					clean("handshake fail", fmt.Sprintf("%s", err))
    94  					return
    95  				}
    96  
    97  				outconnLocalAddr = outconn.LocalAddr().String()
    98  				s.userConns.Set(outconnLocalAddr, &outconn)
    99  				go func() {
   100  					defer func() {
   101  						if e := recover(); e != nil {
   102  							s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack()))
   103  						}
   104  					}()
   105  					buf := make([]byte, 1)
   106  					outconn.SetReadDeadline(time.Time{})
   107  					if _, err := outconn.Read(buf); err != nil {
   108  						clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err))
   109  					}
   110  				}()
   111  				destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr)
   112  				localZeroAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
   113  				outUDPConn, err = net.DialUDP("udp", localZeroAddr, destAddr)
   114  				if err != nil {
   115  					s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr)
   116  					return
   117  				}
   118  				s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn)
   119  				utils.UDPCopy(listener, outUDPConn, srcAddr, time.Second*5, func(data []byte) []byte {
   120  					//forward to local
   121  					var v []byte
   122  					//convert parent data to raw
   123  					if len(s.udpParentKey) > 0 {
   124  						v, err = goaes.Decrypt(s.udpParentKey, data)
   125  						if err != nil {
   126  							s.log.Printf("udp outconn parse packet fail, %s", err.Error())
   127  							return []byte{}
   128  						}
   129  					} else {
   130  						v = data
   131  					}
   132  					return s.localCipher.Encrypt(v[3:])
   133  				}, func(err interface{}) {
   134  					s.udpRelatedPacketConns.Remove(srcAddr.String())
   135  					if err != nil {
   136  						s.log.Printf("udp out->local io copy crashed:\n%s\n%s", err, string(debug.Stack()))
   137  					}
   138  				})
   139  			} else {
   140  				outUDPConn = v.(*net.UDPConn)
   141  			}
   142  			//forward to parent
   143  			//p is raw, now convert it to parent
   144  			var v []byte
   145  			if len(s.udpParentKey) > 0 {
   146  				v, _ = goaes.Encrypt(s.udpParentKey, socksPacket.Bytes())
   147  			} else {
   148  				v = socksPacket.Bytes()
   149  			}
   150  			_, err = outUDPConn.Write(v)
   151  			socksPacket = socks.PacketUDP{}
   152  			if err != nil {
   153  				if utils.IsNetClosedErr(err) {
   154  					return
   155  				}
   156  				s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr)
   157  			}
   158  		}
   159  	}()
   160  	return
   161  }