github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/netlink/netlink_darwin.go (about)

     1  package netlink
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"net"
     7  	"strconv"
     8  	"strings"
     9  	"syscall"
    10  	"unsafe"
    11  
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  const (
    16  	procpidpathinfo     = 0xb
    17  	procpidpathinfosize = 1024
    18  	proccallnumpidinfo  = 0x2
    19  )
    20  
    21  var structSize = func() int {
    22  	value, _ := syscall.Sysctl("kern.osrelease")
    23  	major, _, _ := strings.Cut(value, ".")
    24  	n, _ := strconv.ParseInt(major, 10, 64)
    25  	switch true {
    26  	case n >= 22:
    27  		return 408
    28  	default:
    29  		// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
    30  		// size/offset are round up (aligned) to 8 bytes in darwin
    31  		// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
    32  		// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
    33  		return 384
    34  	}
    35  }()
    36  
    37  func FindProcessName(network string, ip net.IP, port uint16, _ net.IP, _ uint16) (string, error) {
    38  	var spath string
    39  	switch network {
    40  	case "tcp":
    41  		spath = "net.inet.tcp.pcblist_n"
    42  	case "udp":
    43  		spath = "net.inet.udp.pcblist_n"
    44  	default:
    45  		return "", fmt.Errorf("ErrInvalidNetwork: %s", network)
    46  	}
    47  
    48  	isIPv4 := ip.To4() != nil
    49  
    50  	value, err := syscall.Sysctl(spath)
    51  	if err != nil {
    52  		return "", err
    53  	}
    54  
    55  	buf := []byte(value)
    56  	itemSize := structSize
    57  	if network == "tcp" {
    58  		// rup8(sizeof(xtcpcb_n))
    59  		itemSize += 208
    60  	}
    61  
    62  	var fallbackUDPProcess string
    63  	// skip the first xinpgen(24 bytes) block
    64  	for i := 24; i+itemSize <= len(buf); i += itemSize {
    65  		// offset of xinpcb_n and xsocket_n
    66  		inp, so := i, i+104
    67  
    68  		srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20])
    69  		if port != srcPort {
    70  			continue
    71  		}
    72  
    73  		// xinpcb_n.inp_vflag
    74  		flag := buf[inp+44]
    75  
    76  		var (
    77  			srcIP     net.IP
    78  			srcIsIPv4 bool
    79  		)
    80  		switch {
    81  		case flag&0x1 > 0 && isIPv4:
    82  			// ipv4
    83  			srcIP = net.IP(buf[inp+76 : inp+80])
    84  			srcIsIPv4 = true
    85  		case flag&0x2 > 0 && !isIPv4:
    86  			// ipv6
    87  			srcIP = net.IP(buf[inp+64 : inp+80])
    88  		default:
    89  			continue
    90  		}
    91  
    92  		if ip.Equal(srcIP) {
    93  			// xsocket_n.so_last_pid
    94  			pid := readNativeUint32(buf[so+68 : so+72])
    95  			return getExecPathFromPID(pid)
    96  		}
    97  
    98  		// udp packet connection may be not equal with srcIP
    99  		if network == "udp" && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 {
   100  			fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72]))
   101  		}
   102  	}
   103  
   104  	if network == "udp" && fallbackUDPProcess != "" {
   105  		return fallbackUDPProcess, nil
   106  	}
   107  
   108  	return "", fmt.Errorf("not found")
   109  }
   110  
   111  func getExecPathFromPID(pid uint32) (string, error) {
   112  	buf := make([]byte, procpidpathinfosize)
   113  	_, _, errno := syscall.Syscall6(
   114  		syscall.SYS_PROC_INFO,
   115  		proccallnumpidinfo,
   116  		uintptr(pid),
   117  		procpidpathinfo,
   118  		0,
   119  		uintptr(unsafe.Pointer(&buf[0])),
   120  		procpidpathinfosize)
   121  	if errno != 0 {
   122  		return "", errno
   123  	}
   124  
   125  	return unix.ByteSliceToString(buf), nil
   126  }
   127  
   128  func readNativeUint32(b []byte) uint32 {
   129  	return *(*uint32)(unsafe.Pointer(&b[0]))
   130  }