github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/proxy/socks/socks.go (about) 1 package socks 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "net" 8 "strconv" 9 "strings" 10 "sync" 11 12 "github.com/codingeasygo/util/xdebug" 13 "github.com/codingeasygo/util/xio" 14 ) 15 16 // Codable is interface for get current code 17 type Codable interface { 18 Code() byte 19 } 20 21 // Server is an implementation of socks5 proxy 22 type Server struct { 23 BufferSize int 24 listners map[net.Listener]string 25 waiter sync.WaitGroup 26 Dialer xio.PiperDialer 27 } 28 29 // NewServer will return new Server 30 func NewServer() (socks *Server) { 31 socks = &Server{ 32 BufferSize: 32 * 1024, 33 listners: map[net.Listener]string{}, 34 waiter: sync.WaitGroup{}, 35 Dialer: xio.PiperDialerF(xio.DialNetPiper), 36 } 37 return 38 } 39 40 func (s *Server) loopAccept(l net.Listener) { 41 for { 42 conn, err := l.Accept() 43 if err != nil { 44 break 45 } 46 go s.ProcConn(conn) 47 } 48 s.waiter.Done() 49 } 50 51 // Run will listen tcp on address and sync accept to ProcConn 52 func (s *Server) Run(addr string) (err error) { 53 listener, err := net.Listen("tcp", addr) 54 if err == nil { 55 s.listners[listener] = addr 56 InfoLog("Server listen http proxy on %v", addr) 57 s.waiter.Add(1) 58 s.loopAccept(listener) 59 } 60 return 61 } 62 63 // Start proxy listener 64 func (s *Server) Start(network, addr string) (listener net.Listener, err error) { 65 listener, err = net.Listen(network, addr) 66 if err == nil { 67 s.listners[listener] = addr 68 InfoLog("Server listen socks5 proxy on %v", addr) 69 s.waiter.Add(1) 70 go s.loopAccept(listener) 71 } 72 return 73 } 74 75 // Stop will stop listener and wait loop stop 76 func (s *Server) Stop() (err error) { 77 for listener, addr := range s.listners { 78 err = listener.Close() 79 delete(s.listners, listener) 80 InfoLog("Server socks5 proxy listener on %v is stopped by %v", addr, err) 81 } 82 s.waiter.Wait() 83 return 84 } 85 86 // ProcConn will process connecton as socket protocol 87 func (s *Server) ProcConn(conn io.ReadWriteCloser) (err error) { 88 // DebugLog("Server proxy socks connection on %v from %v", xio.LocalAddr(conn), xio.RemoteAddr(conn)) 89 defer func() { 90 if perr := recover(); perr != nil { 91 ErrorLog("Server socks proxy conn on %v from %v is panic with %v, callstakc is \n%v", xio.LocalAddr(conn), xio.RemoteAddr(conn), perr, xdebug.CallStack()) 92 } 93 if err != xio.ErrAsyncRunning { 94 DebugLog("Server socks proxy connection on %v from %v is done with %v", xio.LocalAddr(conn), xio.RemoteAddr(conn), err) 95 conn.Close() 96 } 97 }() 98 buf := make([]byte, 1024*4) 99 // 100 //Procedure method 101 err = xio.FullBuffer(conn, buf, 2, nil) 102 if err != nil { 103 return 104 } 105 if buf[0] != 0x05 { 106 err = fmt.Errorf("only ver 0x05 is supported, but %x", buf[0]) 107 return 108 } 109 err = xio.FullBuffer(conn, buf[2:], uint32(buf[1]), nil) 110 if err != nil { 111 return 112 } 113 _, err = conn.Write([]byte{0x05, 0x00}) 114 if err != nil { 115 return 116 } 117 // 118 //Procedure request 119 err = xio.FullBuffer(conn, buf, 5, nil) 120 if err != nil { 121 return 122 } 123 if buf[0] != 0x05 { 124 err = fmt.Errorf("only ver 0x05 is supported, but %x", buf[0]) 125 return 126 } 127 var uri string 128 switch buf[3] { 129 case 0x01: 130 err = xio.FullBuffer(conn, buf[5:], 5, nil) 131 if err == nil { 132 remote := fmt.Sprintf("%v.%v.%v.%v", buf[4], buf[5], buf[6], buf[7]) 133 port := uint16(buf[8])*256 + uint16(buf[9]) 134 uri = fmt.Sprintf("tcp://%v:%v", remote, port) 135 } 136 case 0x03: 137 err = xio.FullBuffer(conn, buf[5:], uint32(buf[4]+2), nil) 138 if err == nil { 139 remote := string(buf[5 : buf[4]+5]) 140 port := uint16(buf[buf[4]+5])*256 + uint16(buf[buf[4]+6]) 141 uri = fmt.Sprintf("tcp://%v:%v", remote, port) 142 } 143 default: 144 err = xio.FullBuffer(conn, buf[5:], uint32(buf[4]+2), nil) 145 if err == nil { 146 uri = string(buf[5 : buf[4]+5]) 147 } 148 } 149 DebugLog("Server socks proxy start dial to %v on %v from %v", uri, xio.LocalAddr(conn), xio.RemoteAddr(conn)) 150 raw, err := s.Dialer.DialPiper(uri, s.BufferSize) 151 if err != nil { 152 buf[0], buf[1], buf[2], buf[3] = 0x05, 0x04, 0x00, 0x01 153 buf[4], buf[5], buf[6], buf[7] = 0x00, 0x00, 0x00, 0x00 154 buf[8], buf[9] = 0x00, 0x00 155 n := 0 156 if cerr, ok := err.(Codable); ok { 157 buf[1] = cerr.Code() 158 n = 10 159 } else { 160 buf[1] = 0x10 161 message := err.Error() 162 if len(message) > 2048 { 163 message = message[:2048] 164 } 165 binary.BigEndian.PutUint16(buf[10:12], uint16(len(message))) 166 n = 12 + copy(buf[12:], []byte(message)) 167 } 168 conn.Write(buf[:n]) 169 DebugLog("Server socks proxy dial to %v on %v fail with %v", uri, xio.RemoteAddr(conn), err) 170 return 171 } 172 buf[0], buf[1], buf[2], buf[3] = 0x05, 0x00, 0x00, 0x01 173 buf[4], buf[5], buf[6], buf[7] = 0x00, 0x00, 0x00, 0x00 174 buf[8], buf[9] = 0x00, 0x00 175 _, err = conn.Write(buf[:10]) 176 if err != nil { 177 raw.Close() 178 return 179 } 180 err = raw.PipeConn(conn, uri) 181 if err != xio.ErrAsyncRunning { 182 raw.Close() 183 } 184 return 185 } 186 187 // Dial will dial connection by proxy server 188 func Dial(proxy, uri string) (conn net.Conn, err error) { 189 conn, err = DialType(proxy, 0x03, uri) 190 return 191 } 192 193 // DialType wil dial connection by proxy server and uri type 194 func DialType(proxy string, uriType byte, uri string) (conn net.Conn, err error) { 195 proxyNetwork, proxyAddr := "", "" 196 if strings.HasPrefix(proxy, "socks5://") { 197 proxyNetwork = "tcp" 198 proxyAddr = strings.TrimPrefix(proxy, "socks5://") 199 } else if strings.HasPrefix(proxy, "tcp://") { 200 proxyNetwork = "tcp" 201 proxyAddr = strings.TrimPrefix(proxy, "tcp://") 202 } else if strings.HasPrefix(proxy, "unix://") { 203 proxyNetwork = "unix" 204 proxyAddr = strings.TrimPrefix(proxy, "unix://") 205 } else { 206 proxyNetwork = "tcp" 207 proxyAddr = proxy 208 } 209 conn, err = net.Dial(proxyNetwork, proxyAddr) 210 if err != nil { 211 return 212 } 213 conn.Write([]byte{0x05, 0x01, 0x00}) 214 buf := make([]byte, 1024*64) 215 err = xio.FullBuffer(conn, buf, 2, nil) 216 if err != nil { 217 conn.Close() 218 return 219 } 220 if buf[0] != 0x05 || buf[1] != 0x00 { 221 err = fmt.Errorf("unsupported %x", buf) 222 conn.Close() 223 return 224 } 225 var host string 226 var port int 227 if uriType == 0x03 { 228 var p string 229 host, p, _ = net.SplitHostPort(uri) 230 port, _ = strconv.Atoi(p) 231 } else { 232 host = uri 233 } 234 blen := len(host) + 7 235 buf[0], buf[1], buf[2] = 0x05, 0x01, 0x00 236 buf[3], buf[4] = uriType, byte(len(host)) 237 copy(buf[5:], []byte(host)) 238 buf[blen-2] = byte(port / 256) 239 buf[blen-1] = byte(port % 256) 240 conn.Write(buf[:blen]) 241 err = xio.FullBuffer(conn, buf, 5, nil) 242 if err != nil { 243 conn.Close() 244 return 245 } 246 n := 0 247 switch buf[3] { 248 case 0x01: 249 n = 10 250 err = xio.FullBuffer(conn, buf[5:], 5, nil) 251 case 0x03: 252 n = 5 + int(buf[4]) + 2 253 err = xio.FullBuffer(conn, buf[5:], uint32(buf[4])+2, nil) 254 case 0x04: 255 n = 5 + int(buf[4]) + 17 256 err = xio.FullBuffer(conn, buf[5:], 17, nil) 257 default: 258 err = fmt.Errorf("reply address type is not supported:%v", buf[3]) 259 } 260 if err != nil { 261 conn.Close() 262 return 263 } 264 if buf[1] == 0x10 { 265 err = xio.FullBuffer(conn, buf[n:], 2, nil) 266 messageLen := 0 267 if err == nil { 268 messageLen = int(binary.BigEndian.Uint16(buf[n : n+2])) 269 err = xio.FullBuffer(conn, buf[n+2:], uint32(messageLen), nil) 270 } 271 if err == nil { 272 err = fmt.Errorf("%v", string(buf[n+2:n+2+messageLen])) 273 } 274 return 275 } 276 if buf[1] != 0x00 { 277 conn.Close() 278 err = fmt.Errorf("socks server response code(%x)", buf[1]) 279 return 280 } 281 return 282 }