diff --git a/ping/ping.go b/ping/ping.go index 069cc18..c9fdc52 100644 --- a/ping/ping.go +++ b/ping/ping.go @@ -59,28 +59,27 @@ func (c *Conn) isLinuxUnprivileged() bool { func (c *Conn) ReadIP(buffer *buf.Buffer) error { if c.destination.Is6() || c.isLinuxUnprivileged() { var readMsg func(b, oob []byte) (n, oobn int, addr netip.Addr, err error) - switch conn := c.conn.(type) { - case *net.IPConn: + if ipConn, isIPConn := common.Cast[*net.IPConn](c.conn); isIPConn { readMsg = func(b, oob []byte) (n, oobn int, addr netip.Addr, err error) { var ipAddr *net.IPAddr - n, oobn, _, ipAddr, err = conn.ReadMsgIP(b, oob) + n, oobn, _, ipAddr, err = ipConn.ReadMsgIP(b, oob) if err == nil { addr = M.AddrFromNet(ipAddr) } return } - case *net.UDPConn: + } else if udpConn, isUDPConn := common.Cast[*net.UDPConn](c.conn); isUDPConn { readMsg = func(b, oob []byte) (n, oobn int, addr netip.Addr, err error) { var addrPort netip.AddrPort - n, oobn, _, addrPort, err = conn.ReadMsgUDPAddrPort(b, oob) + n, oobn, _, addrPort, err = udpConn.ReadMsgUDPAddrPort(b, oob) if err == nil { addr = addrPort.Addr() } return } - case *UnprivilegedConn: - readMsg = conn.ReadMsg - default: + } else if unprivilegedConn, isUnprivilegedConn := c.conn.(*UnprivilegedConn); isUnprivilegedConn { + readMsg = unprivilegedConn.ReadMsg + } else { return E.New("unsupported conn type: ", reflect.TypeOf(c.conn)) } if !c.destination.Is6() { diff --git a/ping/socket_unix.go b/ping/socket_unix.go index 3e9bc99..1eec10a 100644 --- a/ping/socket_unix.go +++ b/ping/socket_unix.go @@ -9,6 +9,7 @@ import ( "runtime" "syscall" + "github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -76,23 +77,34 @@ func connect(privileged bool, controlFunc control.Func, destination netip.Addr) var bindAddress netip.Addr if !destination.Is6() { - bindAddress = netip.AddrFrom4([4]byte{0, 0, 0, 0}) + bindAddress = netip.AddrFrom4([4]byte{}) } else { bindAddress = netip.AddrFrom16([16]byte{}) } + err = unix.Bind(fd, M.AddrPortToSockaddr(netip.AddrPortFrom(bindAddress, 0))) if err != nil { return nil, err } - err = unix.Connect(fd, M.AddrPortToSockaddr(netip.AddrPortFrom(destination, 0))) - if err != nil { - return nil, E.Cause(err, "connect()") + if runtime.GOOS == "darwin" && !privileged { + // When running in NetworkExtension on macOS, write to connected socket results in EPIPE. + var packetConn net.PacketConn + packetConn, err = net.FilePacketConn(file) + if err != nil { + return nil, err + } + return bufio.NewBindPacketConn(packetConn, M.SocksaddrFrom(destination, 0).UDPAddr()), nil + } else { + err = unix.Connect(fd, M.AddrPortToSockaddr(netip.AddrPortFrom(destination, 0))) + if err != nil { + return nil, err + } + var conn net.Conn + conn, err = net.FileConn(file) + if err != nil { + return nil, err + } + return conn, nil } - - conn, err := net.FileConn(file) - if err != nil { - return nil, err - } - return conn, nil }