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 }