github.com/chwjbn/xclash@v0.2.0/component/process/process_darwin.go (about)

     1  package process
     2  
     3  import (
     4  	"encoding/binary"
     5  	"net"
     6  	"path/filepath"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  const (
    14  	procpidpathinfo     = 0xb
    15  	procpidpathinfosize = 1024
    16  	proccallnumpidinfo  = 0x2
    17  )
    18  
    19  func findProcessName(network string, ip net.IP, port int) (string, error) {
    20  	var spath string
    21  	switch network {
    22  	case TCP:
    23  		spath = "net.inet.tcp.pcblist_n"
    24  	case UDP:
    25  		spath = "net.inet.udp.pcblist_n"
    26  	default:
    27  		return "", ErrInvalidNetwork
    28  	}
    29  
    30  	isIPv4 := ip.To4() != nil
    31  
    32  	value, err := syscall.Sysctl(spath)
    33  	if err != nil {
    34  		return "", err
    35  	}
    36  
    37  	buf := []byte(value)
    38  
    39  	// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
    40  	// size/offset are round up (aligned) to 8 bytes in darwin
    41  	// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
    42  	// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
    43  	itemSize := 384
    44  	if network == TCP {
    45  		// rup8(sizeof(xtcpcb_n))
    46  		itemSize += 208
    47  	}
    48  	// skip the first xinpgen(24 bytes) block
    49  	for i := 24; i+itemSize <= len(buf); i += itemSize {
    50  		// offset of xinpcb_n and xsocket_n
    51  		inp, so := i, i+104
    52  
    53  		srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20])
    54  		if uint16(port) != srcPort {
    55  			continue
    56  		}
    57  
    58  		// xinpcb_n.inp_vflag
    59  		flag := buf[inp+44]
    60  
    61  		var srcIP net.IP
    62  		switch {
    63  		case flag&0x1 > 0 && isIPv4:
    64  			// ipv4
    65  			srcIP = net.IP(buf[inp+76 : inp+80])
    66  		case flag&0x2 > 0 && !isIPv4:
    67  			// ipv6
    68  			srcIP = net.IP(buf[inp+64 : inp+80])
    69  		default:
    70  			continue
    71  		}
    72  
    73  		if !ip.Equal(srcIP) {
    74  			continue
    75  		}
    76  
    77  		// xsocket_n.so_last_pid
    78  		pid := readNativeUint32(buf[so+68 : so+72])
    79  		return getExecPathFromPID(pid)
    80  	}
    81  
    82  	return "", ErrNotFound
    83  }
    84  
    85  func getExecPathFromPID(pid uint32) (string, error) {
    86  	buf := make([]byte, procpidpathinfosize)
    87  	_, _, errno := syscall.Syscall6(
    88  		syscall.SYS_PROC_INFO,
    89  		proccallnumpidinfo,
    90  		uintptr(pid),
    91  		procpidpathinfo,
    92  		0,
    93  		uintptr(unsafe.Pointer(&buf[0])),
    94  		procpidpathinfosize)
    95  	if errno != 0 {
    96  		return "", errno
    97  	}
    98  
    99  	return filepath.Base(unix.ByteSliceToString(buf)), nil
   100  }
   101  
   102  func readNativeUint32(b []byte) uint32 {
   103  	return *(*uint32)(unsafe.Pointer(&b[0]))
   104  }