github.com/kelleygo/clashcore@v1.0.2/transport/hysteria/conns/udp/hop.go (about) 1 package udp 2 3 import ( 4 "errors" 5 "net" 6 "strconv" 7 "strings" 8 "sync" 9 "syscall" 10 "time" 11 12 "github.com/kelleygo/clashcore/transport/hysteria/obfs" 13 "github.com/kelleygo/clashcore/transport/hysteria/utils" 14 15 "github.com/zhangyunhao116/fastrand" 16 ) 17 18 const ( 19 packetQueueSize = 1024 20 ) 21 22 // ObfsUDPHopClientPacketConn is the UDP port-hopping packet connection for client side. 23 // It hops to a different local & server port every once in a while. 24 type ObfsUDPHopClientPacketConn struct { 25 serverAddr net.Addr // Combined udpHopAddr 26 serverAddrs []net.Addr 27 hopInterval time.Duration 28 29 obfs obfs.Obfuscator 30 31 connMutex sync.RWMutex 32 prevConn net.PacketConn 33 currentConn net.PacketConn 34 addrIndex int 35 36 readBufferSize int 37 writeBufferSize int 38 39 recvQueue chan *udpPacket 40 closeChan chan struct{} 41 closed bool 42 43 bufPool sync.Pool 44 } 45 46 type udpHopAddr string 47 48 func (a *udpHopAddr) Network() string { 49 return "udp-hop" 50 } 51 52 func (a *udpHopAddr) String() string { 53 return string(*a) 54 } 55 56 type udpPacket struct { 57 buf []byte 58 n int 59 addr net.Addr 60 } 61 62 func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (net.PacketConn, error) { 63 ports, err := parsePorts(serverPorts) 64 if err != nil { 65 return nil, err 66 } 67 // Resolve the server IP address, then attach the ports to UDP addresses 68 rAddr, err := dialer.RemoteAddr(server) 69 if err != nil { 70 return nil, err 71 } 72 ip, _, err := net.SplitHostPort(rAddr.String()) 73 if err != nil { 74 return nil, err 75 } 76 serverAddrs := make([]net.Addr, len(ports)) 77 for i, port := range ports { 78 serverAddrs[i] = &net.UDPAddr{ 79 IP: net.ParseIP(ip), 80 Port: int(port), 81 } 82 } 83 hopAddr := udpHopAddr(server) 84 conn := &ObfsUDPHopClientPacketConn{ 85 serverAddr: &hopAddr, 86 serverAddrs: serverAddrs, 87 hopInterval: hopInterval, 88 obfs: obfs, 89 addrIndex: fastrand.Intn(len(serverAddrs)), 90 recvQueue: make(chan *udpPacket, packetQueueSize), 91 closeChan: make(chan struct{}), 92 bufPool: sync.Pool{ 93 New: func() interface{} { 94 return make([]byte, udpBufferSize) 95 }, 96 }, 97 } 98 curConn, err := dialer.ListenPacket(rAddr) 99 if err != nil { 100 return nil, err 101 } 102 if obfs != nil { 103 conn.currentConn = NewObfsUDPConn(curConn, obfs) 104 } else { 105 conn.currentConn = curConn 106 } 107 go conn.recvRoutine(conn.currentConn) 108 go conn.hopRoutine(dialer, rAddr) 109 if _, ok := conn.currentConn.(syscall.Conn); ok { 110 return &ObfsUDPHopClientPacketConnWithSyscall{conn}, nil 111 } 112 return conn, nil 113 } 114 115 func (c *ObfsUDPHopClientPacketConn) recvRoutine(conn net.PacketConn) { 116 for { 117 buf := c.bufPool.Get().([]byte) 118 n, addr, err := conn.ReadFrom(buf) 119 if err != nil { 120 return 121 } 122 select { 123 case c.recvQueue <- &udpPacket{buf, n, addr}: 124 default: 125 // Drop the packet if the queue is full 126 c.bufPool.Put(buf) 127 } 128 } 129 } 130 131 func (c *ObfsUDPHopClientPacketConn) hopRoutine(dialer utils.PacketDialer, rAddr net.Addr) { 132 ticker := time.NewTicker(c.hopInterval) 133 defer ticker.Stop() 134 for { 135 select { 136 case <-ticker.C: 137 c.hop(dialer, rAddr) 138 case <-c.closeChan: 139 return 140 } 141 } 142 } 143 144 func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Addr) { 145 c.connMutex.Lock() 146 defer c.connMutex.Unlock() 147 if c.closed { 148 return 149 } 150 newConn, err := dialer.ListenPacket(rAddr) 151 if err != nil { 152 // Skip this hop if failed to listen 153 return 154 } 155 // Close prevConn, 156 // prevConn <- currentConn 157 // currentConn <- newConn 158 // update addrIndex 159 // 160 // We need to keep receiving packets from the previous connection, 161 // because otherwise there will be packet loss due to the time gap 162 // between we hop to a new port and the server acknowledges this change. 163 if c.prevConn != nil { 164 _ = c.prevConn.Close() // recvRoutine will exit on error 165 } 166 c.prevConn = c.currentConn 167 if c.obfs != nil { 168 c.currentConn = NewObfsUDPConn(newConn, c.obfs) 169 } else { 170 c.currentConn = newConn 171 } 172 // Set buffer sizes if previously set 173 if c.readBufferSize > 0 { 174 _ = trySetPacketConnReadBuffer(c.currentConn, c.readBufferSize) 175 } 176 if c.writeBufferSize > 0 { 177 _ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize) 178 } 179 go c.recvRoutine(c.currentConn) 180 c.addrIndex = fastrand.Intn(len(c.serverAddrs)) 181 } 182 183 func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 184 for { 185 select { 186 case p := <-c.recvQueue: 187 /* 188 // Check if the packet is from one of the server addresses 189 for _, addr := range c.serverAddrs { 190 if addr.String() == p.addr.String() { 191 // Copy the packet to the buffer 192 n := copy(b, p.buf[:p.n]) 193 c.bufPool.Put(p.buf) 194 return n, c.serverAddr, nil 195 } 196 } 197 // Drop the packet, continue 198 c.bufPool.Put(p.buf) 199 */ 200 // The above code was causing performance issues when the range is large, 201 // so we skip the check for now. Should probably still check by using a map 202 // or something in the future. 203 n := copy(b, p.buf[:p.n]) 204 c.bufPool.Put(p.buf) 205 return n, c.serverAddr, nil 206 case <-c.closeChan: 207 return 0, nil, net.ErrClosed 208 } 209 // Ignore packets from other addresses 210 } 211 } 212 213 func (c *ObfsUDPHopClientPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { 214 c.connMutex.RLock() 215 defer c.connMutex.RUnlock() 216 if c.closed { 217 return 0, net.ErrClosed 218 } 219 /* 220 // Check if the address is the server address 221 if addr.String() != c.serverAddr.String() { 222 return 0, net.ErrWriteToConnected 223 } 224 */ 225 // Skip the check for now, always write to the server 226 return c.currentConn.WriteTo(b, c.serverAddrs[c.addrIndex]) 227 } 228 229 func (c *ObfsUDPHopClientPacketConn) Close() error { 230 c.connMutex.Lock() 231 defer c.connMutex.Unlock() 232 if c.closed { 233 return nil 234 } 235 // Close prevConn and currentConn 236 // Close closeChan to unblock ReadFrom & hopRoutine 237 // Set closed flag to true to prevent double close 238 if c.prevConn != nil { 239 _ = c.prevConn.Close() 240 } 241 err := c.currentConn.Close() 242 close(c.closeChan) 243 c.closed = true 244 c.serverAddrs = nil // For GC 245 return err 246 } 247 248 func (c *ObfsUDPHopClientPacketConn) LocalAddr() net.Addr { 249 c.connMutex.RLock() 250 defer c.connMutex.RUnlock() 251 return c.currentConn.LocalAddr() 252 } 253 254 func (c *ObfsUDPHopClientPacketConn) SetReadDeadline(t time.Time) error { 255 // Not supported 256 return nil 257 } 258 259 func (c *ObfsUDPHopClientPacketConn) SetWriteDeadline(t time.Time) error { 260 // Not supported 261 return nil 262 } 263 264 func (c *ObfsUDPHopClientPacketConn) SetDeadline(t time.Time) error { 265 err := c.SetReadDeadline(t) 266 if err != nil { 267 return err 268 } 269 return c.SetWriteDeadline(t) 270 } 271 272 func (c *ObfsUDPHopClientPacketConn) SetReadBuffer(bytes int) error { 273 c.connMutex.Lock() 274 defer c.connMutex.Unlock() 275 c.readBufferSize = bytes 276 if c.prevConn != nil { 277 _ = trySetPacketConnReadBuffer(c.prevConn, bytes) 278 } 279 return trySetPacketConnReadBuffer(c.currentConn, bytes) 280 } 281 282 func (c *ObfsUDPHopClientPacketConn) SetWriteBuffer(bytes int) error { 283 c.connMutex.Lock() 284 defer c.connMutex.Unlock() 285 c.writeBufferSize = bytes 286 if c.prevConn != nil { 287 _ = trySetPacketConnWriteBuffer(c.prevConn, bytes) 288 } 289 return trySetPacketConnWriteBuffer(c.currentConn, bytes) 290 } 291 292 func trySetPacketConnReadBuffer(pc net.PacketConn, bytes int) error { 293 sc, ok := pc.(interface { 294 SetReadBuffer(bytes int) error 295 }) 296 if ok { 297 return sc.SetReadBuffer(bytes) 298 } 299 return nil 300 } 301 302 func trySetPacketConnWriteBuffer(pc net.PacketConn, bytes int) error { 303 sc, ok := pc.(interface { 304 SetWriteBuffer(bytes int) error 305 }) 306 if ok { 307 return sc.SetWriteBuffer(bytes) 308 } 309 return nil 310 } 311 312 type ObfsUDPHopClientPacketConnWithSyscall struct { 313 *ObfsUDPHopClientPacketConn 314 } 315 316 func (c *ObfsUDPHopClientPacketConnWithSyscall) SyscallConn() (syscall.RawConn, error) { 317 c.connMutex.RLock() 318 defer c.connMutex.RUnlock() 319 sc, ok := c.currentConn.(syscall.Conn) 320 if !ok { 321 return nil, errors.New("not supported") 322 } 323 return sc.SyscallConn() 324 } 325 326 // parsePorts parses the multi-port server address and returns the host and ports. 327 // Supports both comma-separated single ports and dash-separated port ranges. 328 // Format: "host:port1,port2-port3,port4" 329 func parsePorts(serverPorts string) (ports []uint16, err error) { 330 portStrs := strings.Split(serverPorts, ",") 331 for _, portStr := range portStrs { 332 if strings.Contains(portStr, "-") { 333 // Port range 334 portRange := strings.Split(portStr, "-") 335 if len(portRange) != 2 { 336 return nil, net.InvalidAddrError("invalid port range") 337 } 338 start, err := strconv.ParseUint(portRange[0], 10, 16) 339 if err != nil { 340 return nil, net.InvalidAddrError("invalid port range") 341 } 342 end, err := strconv.ParseUint(portRange[1], 10, 16) 343 if err != nil { 344 return nil, net.InvalidAddrError("invalid port range") 345 } 346 if start > end { 347 start, end = end, start 348 } 349 for i := start; i <= end; i++ { 350 ports = append(ports, uint16(i)) 351 } 352 } else { 353 // Single port 354 port, err := strconv.ParseUint(portStr, 10, 16) 355 if err != nil { 356 return nil, net.InvalidAddrError("invalid port") 357 } 358 ports = append(ports, uint16(port)) 359 } 360 } 361 if len(ports) == 0 { 362 return nil, net.InvalidAddrError("invalid port") 363 } 364 return ports, nil 365 }