mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-09-27 04:55:53 +08:00
88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
//go:build unix
|
|
|
|
package ping
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
|
|
"github.com/sagernet/sing/common/control"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func connect(privileged bool, controlFunc control.Func, destination netip.Addr) (net.Conn, error) {
|
|
var (
|
|
network string
|
|
fd int
|
|
err error
|
|
)
|
|
if destination.Is4() {
|
|
network = "ip4:icmp"
|
|
if !privileged {
|
|
fd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_ICMP)
|
|
} else {
|
|
fd, err = unix.Socket(unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_ICMP)
|
|
}
|
|
} else {
|
|
network = "ip6:icmp"
|
|
if !privileged {
|
|
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_ICMPV6)
|
|
} else {
|
|
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_ICMPV6)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, E.Cause(err, "socket()")
|
|
}
|
|
file := os.NewFile(uintptr(fd), "datagram-oriented icmp")
|
|
defer file.Close()
|
|
err = unix.Connect(fd, M.AddrPortToSockaddr(netip.AddrPortFrom(destination, 0)))
|
|
if err != nil {
|
|
return nil, E.Cause(err, "connect()")
|
|
}
|
|
|
|
if destination.Is4() && runtime.GOOS == "linux" {
|
|
//err = unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
|
|
//if err != nil {
|
|
// return nil, err
|
|
//}
|
|
err = unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVTTL, 1)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "setsockopt()")
|
|
}
|
|
}
|
|
if destination.Is6() {
|
|
err = unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "setsockopt()")
|
|
}
|
|
err = unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "setsockopt()")
|
|
}
|
|
}
|
|
|
|
conn, err := net.FileConn(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if controlFunc != nil {
|
|
var syscallConn syscall.RawConn
|
|
syscallConn, err = conn.(syscall.Conn).SyscallConn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = controlFunc(network, destination.String(), syscallConn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return conn, nil
|
|
}
|