github.com/yaling888/clash@v1.53.0/component/process/process_linux.go (about) 1 package process 2 3 import ( 4 "bytes" 5 "fmt" 6 "net" 7 "net/netip" 8 "os" 9 10 "github.com/vishvananda/netlink" 11 "github.com/vishvananda/netlink/nl" 12 "golang.org/x/sys/unix" 13 14 "github.com/yaling888/clash/common/pool" 15 ) 16 17 const ( 18 sizeofSocketID = 0x30 19 sizeofSocket = sizeofSocketID + 0x18 20 ) 21 22 type socketRequest struct { 23 Family uint8 24 Protocol uint8 25 Ext uint8 26 pad uint8 27 States uint32 28 ID netlink.SocketID 29 } 30 31 func (r *socketRequest) serialize(bp *pool.BufferWriter) { 32 bp.PutUint8(r.Family) 33 bp.PutUint8(r.Protocol) 34 bp.PutUint8(r.Ext) 35 bp.PutUint8(r.pad) 36 bp.PutUint32(r.States) 37 bp.PutUint16be(r.ID.SourcePort) 38 bp.PutUint16be(r.ID.DestinationPort) 39 if r.Family == unix.AF_INET6 { 40 bp.PutIPv6(r.ID.Source) 41 bp.PutIPv6(r.ID.Destination) 42 } else { 43 bp.PutIPv4(r.ID.Source) 44 bp.Grow(12) 45 bp.PutIPv4(r.ID.Destination) 46 bp.Grow(12) 47 } 48 bp.PutUint32(r.ID.Interface) 49 bp.PutUint32(r.ID.Cookie[0]) 50 bp.PutUint32(r.ID.Cookie[1]) 51 } 52 53 func findProcessPath(network string, from netip.AddrPort, to netip.AddrPort) (string, error) { 54 inode, uid, err := resolveSocketByNetlink(network, from, to) 55 if err != nil { 56 return "", err 57 } 58 59 return resolveProcessPathByProcSearch(inode, uid) 60 } 61 62 func resolveSocketByNetlink(network string, from netip.AddrPort, to netip.AddrPort) (inode uint32, uid uint32, err error) { 63 var families []byte 64 if from.Addr().Unmap().Is4() { 65 families = []byte{unix.AF_INET, unix.AF_INET6} 66 } else { 67 families = []byte{unix.AF_INET6, unix.AF_INET} 68 } 69 70 var protocol byte 71 switch network { 72 case TCP: 73 protocol = unix.IPPROTO_TCP 74 case UDP: 75 protocol = unix.IPPROTO_UDP 76 default: 77 return 0, 0, ErrInvalidNetwork 78 } 79 80 if protocol == unix.IPPROTO_UDP { 81 from, to = to, from 82 } 83 84 for _, family := range families { 85 inode, uid, err = resolveSocketByNetlinkExact(family, protocol, from, to) 86 if err == nil { 87 return inode, uid, err 88 } 89 } 90 91 return 0, 0, ErrNotFound 92 } 93 94 func resolveSocketByNetlinkExact(family byte, protocol byte, fromAddr netip.AddrPort, toAddr netip.AddrPort) (uint32, uint32, error) { 95 var ( 96 fromIP net.IP 97 toIP net.IP 98 ) 99 if family == unix.AF_INET { 100 fromIP = net.IP(fromAddr.Addr().AsSlice()).To4() 101 toIP = net.IP(toAddr.Addr().AsSlice()).To4() 102 } else { 103 fromIP = net.IP(fromAddr.Addr().AsSlice()).To16() 104 toIP = net.IP(toAddr.Addr().AsSlice()).To16() 105 } 106 107 s, err := nl.Subscribe(unix.NETLINK_INET_DIAG) 108 if err != nil { 109 return 0, 0, err 110 } 111 defer s.Close() 112 113 bufP := pool.GetBufferWriter() 114 defer pool.PutBufferWriter(bufP) 115 116 request := &socketRequest{ 117 Family: family, 118 Protocol: protocol, 119 ID: netlink.SocketID{ 120 SourcePort: fromAddr.Port(), 121 DestinationPort: toAddr.Port(), 122 Source: fromIP, 123 Destination: toIP, 124 Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE}, 125 }, 126 } 127 request.serialize(bufP) 128 129 req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, 0) // unix.NLM_F_DUMP 130 req.AddRawData(bufP.Bytes()) 131 132 err = s.Send(req) 133 if err != nil { 134 return 0, 0, err 135 } 136 137 msgs, from, err := s.Receive() 138 if err != nil { 139 return 0, 0, err 140 } 141 142 if from.Pid != nl.PidKernel { 143 return 0, 0, fmt.Errorf("wrong sender portid %d, expected %d", from.Pid, nl.PidKernel) 144 } 145 if len(msgs) == 0 { 146 return 0, 0, fmt.Errorf("no message nor error from netlink") 147 } 148 if len(msgs) > 2 { 149 return 0, 0, fmt.Errorf("multiple (%d) matching sockets", len(msgs)) 150 } 151 152 inode, uid, err := deserialize(msgs[0].Data) 153 if err != nil { 154 return 0, 0, ErrNotFound 155 } 156 157 return inode, uid, nil 158 } 159 160 func resolveProcessPathByProcSearch(inode, uid uint32) (string, error) { 161 const ( 162 path = "/proc/" 163 pathLen = len(path) 164 ) 165 procDir, err := os.Open(path) 166 if err != nil { 167 return "", err 168 } 169 defer func(procDir *os.File) { 170 _ = procDir.Close() 171 }(procDir) 172 173 pids, err := procDir.Readdirnames(-1) 174 if err != nil { 175 return "", err 176 } 177 178 expectedSocketName := fmt.Appendf(nil, "socket:[%d]", inode) 179 180 pathBuffer := pool.GetBufferWriter() 181 defer pool.PutBufferWriter(pathBuffer) 182 183 readlinkBuffer := pool.GetBufferWriter() 184 readlinkBuffer.Grow(32) 185 defer pool.PutBufferWriter(readlinkBuffer) 186 187 pathBuffer.PutString(path) 188 for _, pid := range pids { 189 if !isPid(pid) { 190 continue 191 } 192 193 pathBuffer. 194 Truncate(pathLen). 195 PutString(pid) 196 197 stat := &unix.Stat_t{} 198 err = unix.Stat(pathBuffer.String(), stat) 199 if err != nil { 200 continue 201 } else if stat.Uid != uid { 202 continue 203 } 204 205 pathBuffer.PutString("/fd/") 206 fdsPrefixLength := pathBuffer.Len() 207 208 fdDir, err := os.Open(pathBuffer.String()) 209 if err != nil { 210 continue 211 } 212 213 fds, err := fdDir.Readdirnames(-1) 214 _ = fdDir.Close() 215 if err != nil { 216 continue 217 } 218 219 for _, fd := range fds { 220 pathBuffer. 221 Truncate(fdsPrefixLength). 222 PutString(fd) 223 224 n, err := unix.Readlink(pathBuffer.String(), *readlinkBuffer) 225 if err != nil { 226 continue 227 } 228 229 if bytes.Equal((*readlinkBuffer)[:n], expectedSocketName) { 230 return os.Readlink(path + pid + "/exe") 231 } 232 } 233 } 234 235 return "", fmt.Errorf("inode %d of uid %d not found", inode, uid) 236 } 237 238 func isPid(name string) bool { 239 for _, c := range name { 240 if c < '0' || c > '9' { 241 return false 242 } 243 } 244 245 return true 246 } 247 248 func deserialize(b []byte) (inode, uid uint32, err error) { 249 if len(b) < sizeofSocket { 250 return 0, 0, fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket) 251 } 252 buf := pool.BufferReader(b) 253 buf.Skip(64) 254 uid = buf.ReadUint32() 255 inode = buf.ReadUint32() 256 return 257 }