JackDoan 6 месяцев назад
Родитель
Сommit
4c4faa3fac
1 измененных файлов с 70 добавлено и 2 удалено
  1. 70 2
      udp/udp_linux.go

+ 70 - 2
udp/udp_linux.go

@@ -21,6 +21,7 @@ import (
 type StdConn struct {
 	udpConn *net.UDPConn
 	rawConn syscall.RawConn
+	sockFd  int
 	isV4    bool
 	l       *logrus.Logger
 	batch   int
@@ -61,9 +62,20 @@ func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch in
 	}
 	udpConn.LocalAddr().String()
 
+	//steal the socket's fd for sending
+	var sockFd uintptr
+	err = rawConn.Control(func(fd uintptr) {
+		sockFd = fd
+	})
+	if err != nil {
+		_ = udpConn.Close()
+		return nil, err
+	}
+
 	return &StdConn{
 		udpConn: udpConn,
 		rawConn: rawConn,
+		sockFd:  int(sockFd),
 		isV4:    ip.Is4(),
 		l:       l,
 		batch:   batch,
@@ -223,8 +235,64 @@ func (u *StdConn) ListenOut(r EncReader) {
 }
 
 func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
-	_, err := u.udpConn.WriteToUDPAddrPort(b, ip)
-	return err
+	if u.isV4 {
+		return u.writeTo4(b, ip)
+	}
+	return u.writeTo6(b, ip)
+}
+
+func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error {
+	var rsa unix.RawSockaddrInet6
+	rsa.Family = unix.AF_INET6
+	rsa.Addr = ip.Addr().As16()
+	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
+
+	for {
+		_, _, err := unix.Syscall6(
+			unix.SYS_SENDTO,
+			uintptr(u.sockFd),
+			uintptr(unsafe.Pointer(&b[0])),
+			uintptr(len(b)),
+			uintptr(0),
+			uintptr(unsafe.Pointer(&rsa)),
+			uintptr(unix.SizeofSockaddrInet6),
+		)
+
+		if err != 0 {
+			return &net.OpError{Op: "sendto", Err: err}
+		}
+
+		return nil
+	}
+}
+
+func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error {
+	if !ip.Addr().Is4() {
+		return ErrInvalidIPv6RemoteForSocket
+	}
+
+	var rsa unix.RawSockaddrInet4
+	rsa.Family = unix.AF_INET
+	rsa.Addr = ip.Addr().As4()
+	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
+
+	for {
+		_, _, err := unix.Syscall6(
+			unix.SYS_SENDTO,
+			uintptr(u.sockFd),
+			uintptr(unsafe.Pointer(&b[0])),
+			uintptr(len(b)),
+			uintptr(0),
+			uintptr(unsafe.Pointer(&rsa)),
+			uintptr(unix.SizeofSockaddrInet4),
+		)
+
+		if err != 0 {
+			return &net.OpError{Op: "sendto", Err: err}
+		}
+
+		return nil
+	}
 }
 
 func (u *StdConn) ReloadConfig(c *config.C) {