64 lines
1.3 KiB
Go
64 lines
1.3 KiB
Go
//go:build !windows
|
|
|
|
package packet
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/Dreamacro/clash/common/pool"
|
|
)
|
|
|
|
type enhanceUDPConn struct {
|
|
*net.UDPConn
|
|
rawConn syscall.RawConn
|
|
}
|
|
|
|
func (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
|
|
if c.rawConn == nil {
|
|
c.rawConn, _ = c.UDPConn.SyscallConn()
|
|
}
|
|
var readErr error
|
|
err = c.rawConn.Read(func(fd uintptr) (done bool) {
|
|
readBuf := pool.Get(pool.UDPBufferSize)
|
|
put = func() {
|
|
_ = pool.Put(readBuf)
|
|
}
|
|
var readFrom syscall.Sockaddr
|
|
var readN int
|
|
readN, _, _, readFrom, readErr = syscall.Recvmsg(int(fd), readBuf, nil, 0)
|
|
if readN > 0 {
|
|
data = readBuf[:readN]
|
|
} else {
|
|
put()
|
|
put = nil
|
|
}
|
|
if readErr == syscall.EAGAIN {
|
|
return false
|
|
}
|
|
if readFrom != nil {
|
|
switch from := readFrom.(type) {
|
|
case *syscall.SockaddrInet4:
|
|
ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes
|
|
addr = &net.UDPAddr{IP: ip[:], Port: from.Port}
|
|
case *syscall.SockaddrInet6:
|
|
ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes
|
|
addr = &net.UDPAddr{IP: ip[:], Port: from.Port, Zone: strconv.FormatInt(int64(from.ZoneId), 10)}
|
|
}
|
|
}
|
|
if readN == 0 {
|
|
readErr = io.EOF
|
|
}
|
|
return true
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if readErr != nil {
|
|
err = readErr
|
|
return
|
|
}
|
|
return
|
|
}
|