github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/netlink/netlink_linux.go (about) 1 package netlink 2 3 import ( 4 "bytes" 5 "fmt" 6 "net" 7 "os" 8 9 "github.com/Asutorufa/yuhaiin/pkg/utils/pool" 10 "github.com/vishvananda/netlink" 11 "golang.org/x/sys/unix" 12 ) 13 14 /* 15 https://stackoverflow.com/questions/10996242/how-to-get-the-pid-of-a-process-that-is-listening-on-a-certain-port-programmatic 16 17 Like netstat, you should read /proc/net/tcp. 18 19 Interpreting it: 20 21 The second field, titled local_address, is the IP and port. 00000000:0050 would be HTTP (the port number is in hex). 22 The 4th field, titled st, is the state. A is TCP_LISTEN. 23 The 10th field, titled inode is the inode number (decimal this time). 24 For each process, /proc/pid/fd/ contains an entry for each open file descriptor. ls -l for socket descriptors shows that it's a link to socket:[nnnnnn]. The number nnnnnn should match the inode number from /proc/net/tcp. 25 26 This makes finding the process quite tiresome, but possible. 27 Finding the right line in /proc/net/tcp isn't difficult, and then you can get the inode number. 28 Finding the process requires you to scan all processes, looking for one which refers this inode number. I know no better way. 29 */ 30 31 func FindProcessName(network string, ip net.IP, srcPort uint16, to net.IP, toPort uint16) (string, error) { 32 var addr net.Addr 33 var remote []net.Addr 34 35 if len(network) < 3 { 36 return "", fmt.Errorf("ErrInvalidNetwork: %s", network) 37 } 38 39 network = network[0:3] 40 41 switch network { 42 case "tcp": 43 addr = &net.TCPAddr{IP: ip, Port: int(srcPort)} 44 remote = []net.Addr{&net.TCPAddr{IP: to, Port: int(toPort)}} 45 case "udp": 46 addr = &net.UDPAddr{IP: ip, Port: int(srcPort)} 47 remote = []net.Addr{ 48 // &net.UDPAddr{IP: to, Port: int(toPort)}, 49 &net.UDPAddr{IP: net.IPv6zero, Port: 0}, 50 &net.UDPAddr{IP: net.IPv4zero, Port: 0}, 51 } 52 default: 53 return "", fmt.Errorf("ErrInvalidNetwork: %s", network) 54 } 55 56 var st *netlink.Socket 57 var err error 58 59 for _, r := range remote { 60 st, err = netlink.SocketGet(addr, r) 61 if err == nil { 62 break 63 } 64 } 65 66 if st == nil { 67 return "", err 68 } 69 70 return resolveProcessNameByProcSearch(st.INode, st.UID) 71 } 72 73 func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { 74 procDir, err := os.Open("/proc") 75 if err != nil { 76 return "", err 77 } 78 defer procDir.Close() 79 80 pids, err := procDir.Readdirnames(-1) 81 if err != nil { 82 return "", err 83 } 84 85 expectedSocketName := fmt.Appendf(nil, "socket:[%d]", inode) 86 87 pathBuffer := pool.GetBuffer() 88 defer pool.PutBuffer(pathBuffer) 89 90 readlinkBuffer := pool.GetBytesBuffer(32) 91 defer readlinkBuffer.Free() 92 93 pathBuffer.WriteString("/proc/") 94 95 for _, pid := range pids { 96 if !isPid(pid) { 97 continue 98 } 99 100 pathBuffer.Truncate(len("/proc/")) 101 pathBuffer.WriteString(pid) 102 103 stat := &unix.Stat_t{} 104 err = unix.Stat(pathBuffer.String(), stat) 105 if err != nil { 106 continue 107 } 108 109 if stat.Uid != uid { 110 continue 111 } 112 113 pathBuffer.WriteString("/fd/") 114 fdsPrefixLength := pathBuffer.Len() 115 116 fdDir, err := os.Open(pathBuffer.String()) 117 if err != nil { 118 continue 119 } 120 121 fds, err := fdDir.Readdirnames(-1) 122 fdDir.Close() 123 if err != nil { 124 continue 125 } 126 127 for _, fd := range fds { 128 pathBuffer.Truncate(fdsPrefixLength) 129 pathBuffer.WriteString(fd) 130 131 n, err := unix.Readlink(pathBuffer.String(), readlinkBuffer.Bytes()) 132 if err != nil { 133 continue 134 } 135 136 if bytes.Equal(readlinkBuffer.Bytes()[:n], expectedSocketName) { 137 return os.Readlink("/proc/" + pid + "/exe") 138 } 139 } 140 } 141 142 return "", fmt.Errorf("inode %d of uid %d not found", inode, uid) 143 } 144 145 func isPid(name string) bool { 146 for _, c := range name { 147 if c < '0' || c > '9' { 148 return false 149 } 150 } 151 152 return true 153 }