github.com/igoogolx/clash@v1.19.8/component/process/process_freebsd.go (about)

     1  package process
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"net/netip"
     7  	"strconv"
     8  	"strings"
     9  	"unsafe"
    10  
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  type Xinpgen12 [64]byte // size 64
    15  
    16  type InEndpoints12 struct {
    17  	FPort  [2]byte
    18  	LPort  [2]byte
    19  	FAddr  [16]byte
    20  	LAddr  [16]byte
    21  	ZoneID uint32
    22  } // size 40
    23  
    24  type XTcpcb12 struct {
    25  	Len         uint32        // offset 0
    26  	_           [20]byte      // offset 4
    27  	SocketAddr  uint64        // offset 24
    28  	_           [84]byte      // offset 32
    29  	Family      uint32        // offset 116
    30  	_           [140]byte     // offset 120
    31  	InEndpoints InEndpoints12 // offset 260
    32  	_           [444]byte     // offset 300
    33  } // size 744
    34  
    35  type XInpcb12 struct {
    36  	Len         uint32        // offset 0
    37  	_           [12]byte      // offset 4
    38  	SocketAddr  uint64        // offset 16
    39  	_           [84]byte      // offset 24
    40  	Family      uint32        // offset 108
    41  	_           [140]byte     // offset 112
    42  	InEndpoints InEndpoints12 // offset 252
    43  	_           [108]byte     // offset 292
    44  } // size 400
    45  
    46  type XFile12 struct {
    47  	Size     uint64   // offset 0
    48  	Pid      uint32   // offset 8
    49  	_        [44]byte // offset 12
    50  	DataAddr uint64   // offset 56
    51  	_        [64]byte // offset 64
    52  } // size 128
    53  
    54  var majorVersion = func() int {
    55  	releaseVersion, err := unix.Sysctl("kern.osrelease")
    56  	if err != nil {
    57  		return 0
    58  	}
    59  
    60  	majorVersionText, _, _ := strings.Cut(releaseVersion, ".")
    61  
    62  	majorVersion, err := strconv.Atoi(majorVersionText)
    63  	if err != nil {
    64  		return 0
    65  	}
    66  
    67  	return majorVersion
    68  }()
    69  
    70  func findProcessPath(network string, from netip.AddrPort, to netip.AddrPort) (string, error) {
    71  	switch majorVersion {
    72  	case 12, 13:
    73  		return findProcessPath12(network, from, to)
    74  	}
    75  
    76  	return "", ErrPlatformNotSupport
    77  }
    78  
    79  func findProcessPath12(network string, from netip.AddrPort, to netip.AddrPort) (string, error) {
    80  	switch network {
    81  	case TCP:
    82  		data, err := unix.SysctlRaw("net.inet.tcp.pcblist")
    83  		if err != nil {
    84  			return "", err
    85  		}
    86  
    87  		if len(data) < int(unsafe.Sizeof(Xinpgen12{})) {
    88  			return "", fmt.Errorf("invalid sysctl data len: %d", len(data))
    89  		}
    90  
    91  		data = data[unsafe.Sizeof(Xinpgen12{}):]
    92  
    93  		for len(data) > int(unsafe.Sizeof(XTcpcb12{}.Len)) {
    94  			tcb := (*XTcpcb12)(unsafe.Pointer(&data[0]))
    95  			if tcb.Len < uint32(unsafe.Sizeof(XTcpcb12{})) || uint32(len(data)) < tcb.Len {
    96  				break
    97  			}
    98  
    99  			data = data[tcb.Len:]
   100  
   101  			var (
   102  				connFromAddr netip.Addr
   103  				connToAddr   netip.Addr
   104  			)
   105  			if tcb.Family == unix.AF_INET {
   106  				connFromAddr = netip.AddrFrom4([4]byte(tcb.InEndpoints.LAddr[12:16]))
   107  				connToAddr = netip.AddrFrom4([4]byte(tcb.InEndpoints.FAddr[12:16]))
   108  			} else if tcb.Family == unix.AF_INET6 {
   109  				connFromAddr = netip.AddrFrom16(tcb.InEndpoints.LAddr)
   110  				connToAddr = netip.AddrFrom16(tcb.InEndpoints.FAddr)
   111  			} else {
   112  				continue
   113  			}
   114  
   115  			connFrom := netip.AddrPortFrom(connFromAddr, binary.BigEndian.Uint16(tcb.InEndpoints.LPort[:]))
   116  			connTo := netip.AddrPortFrom(connToAddr, binary.BigEndian.Uint16(tcb.InEndpoints.FPort[:]))
   117  
   118  			if connFrom == from && connTo == to {
   119  				pid, err := findPidBySocketAddr12(tcb.SocketAddr)
   120  				if err != nil {
   121  					return "", err
   122  				}
   123  
   124  				return findExecutableByPid(pid)
   125  			}
   126  		}
   127  	case UDP:
   128  		data, err := unix.SysctlRaw("net.inet.udp.pcblist")
   129  		if err != nil {
   130  			return "", err
   131  		}
   132  
   133  		if len(data) < int(unsafe.Sizeof(Xinpgen12{})) {
   134  			return "", fmt.Errorf("invalid sysctl data len: %d", len(data))
   135  		}
   136  
   137  		data = data[unsafe.Sizeof(Xinpgen12{}):]
   138  
   139  		for len(data) > int(unsafe.Sizeof(XInpcb12{}.Len)) {
   140  			icb := (*XInpcb12)(unsafe.Pointer(&data[0]))
   141  			if icb.Len < uint32(unsafe.Sizeof(XInpcb12{})) || uint32(len(data)) < icb.Len {
   142  				break
   143  			}
   144  			data = data[icb.Len:]
   145  
   146  			var connFromAddr netip.Addr
   147  			if icb.Family == unix.AF_INET {
   148  				connFromAddr = netip.AddrFrom4([4]byte(icb.InEndpoints.LAddr[12:16]))
   149  			} else if icb.Family == unix.AF_INET6 {
   150  				connFromAddr = netip.AddrFrom16(icb.InEndpoints.LAddr)
   151  			} else {
   152  				continue
   153  			}
   154  
   155  			connFromPort := binary.BigEndian.Uint16(icb.InEndpoints.LPort[:])
   156  
   157  			if (connFromAddr == from.Addr() || connFromAddr.IsUnspecified()) && connFromPort == from.Port() {
   158  				pid, err := findPidBySocketAddr12(icb.SocketAddr)
   159  				if err != nil {
   160  					return "", err
   161  				}
   162  
   163  				return findExecutableByPid(pid)
   164  			}
   165  		}
   166  	}
   167  
   168  	return "", ErrNotFound
   169  }
   170  
   171  func findPidBySocketAddr12(socketAddr uint64) (uint32, error) {
   172  	buf, err := unix.SysctlRaw("kern.file")
   173  	if err != nil {
   174  		return 0, err
   175  	}
   176  
   177  	filesLen := len(buf) / int(unsafe.Sizeof(XFile12{}))
   178  	files := unsafe.Slice((*XFile12)(unsafe.Pointer(&buf[0])), filesLen)
   179  
   180  	for _, file := range files {
   181  		if file.Size != uint64(unsafe.Sizeof(XFile12{})) {
   182  			return 0, fmt.Errorf("invalid xfile size: %d", file.Size)
   183  		}
   184  
   185  		if file.DataAddr == socketAddr {
   186  			return file.Pid, nil
   187  		}
   188  	}
   189  
   190  	return 0, ErrNotFound
   191  }
   192  
   193  func findExecutableByPid(pid uint32) (string, error) {
   194  	buf := make([]byte, unix.PathMax)
   195  	size := uint64(len(buf))
   196  	mib := [4]uint32{
   197  		unix.CTL_KERN,
   198  		14, // KERN_PROC
   199  		12, // KERN_PROC_PATHNAME
   200  		pid,
   201  	}
   202  
   203  	_, _, errno := unix.Syscall6(
   204  		unix.SYS___SYSCTL,
   205  		uintptr(unsafe.Pointer(&mib[0])),
   206  		uintptr(len(mib)),
   207  		uintptr(unsafe.Pointer(&buf[0])),
   208  		uintptr(unsafe.Pointer(&size)),
   209  		0,
   210  		0,
   211  	)
   212  	if errno != 0 || size == 0 {
   213  		return "", fmt.Errorf("sysctl: get proc name: %w", errno)
   214  	}
   215  
   216  	return string(buf[:size-1]), nil
   217  }