github.com/sagernet/sing-box@v1.2.7/common/process/searcher_linux_shared.go (about) 1 //go:build linux 2 3 package process 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 "fmt" 9 "net" 10 "net/netip" 11 "os" 12 "path" 13 "strings" 14 "syscall" 15 "unicode" 16 "unsafe" 17 18 "github.com/sagernet/sing/common" 19 "github.com/sagernet/sing/common/buf" 20 E "github.com/sagernet/sing/common/exceptions" 21 N "github.com/sagernet/sing/common/network" 22 ) 23 24 // from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62 25 var nativeEndian = func() binary.ByteOrder { 26 var x uint32 = 0x01020304 27 if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 28 return binary.BigEndian 29 } 30 31 return binary.LittleEndian 32 }() 33 34 const ( 35 sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48 36 socketDiagByFamily = 20 37 pathProc = "/proc" 38 ) 39 40 func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (inode, uid uint32, err error) { 41 var family uint8 42 var protocol uint8 43 44 switch network { 45 case N.NetworkTCP: 46 protocol = syscall.IPPROTO_TCP 47 case N.NetworkUDP: 48 protocol = syscall.IPPROTO_UDP 49 default: 50 return 0, 0, os.ErrInvalid 51 } 52 53 if source.Addr().Is4() { 54 family = syscall.AF_INET 55 } else { 56 family = syscall.AF_INET6 57 } 58 59 req := packSocketDiagRequest(family, protocol, source) 60 61 socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG) 62 if err != nil { 63 return 0, 0, E.Cause(err, "dial netlink") 64 } 65 defer syscall.Close(socket) 66 67 syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100}) 68 syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100}) 69 70 err = syscall.Connect(socket, &syscall.SockaddrNetlink{ 71 Family: syscall.AF_NETLINK, 72 Pad: 0, 73 Pid: 0, 74 Groups: 0, 75 }) 76 if err != nil { 77 return 78 } 79 80 _, err = syscall.Write(socket, req) 81 if err != nil { 82 return 0, 0, E.Cause(err, "write netlink request") 83 } 84 85 _buffer := buf.StackNew() 86 defer common.KeepAlive(_buffer) 87 buffer := common.Dup(_buffer) 88 defer buffer.Release() 89 90 n, err := syscall.Read(socket, buffer.FreeBytes()) 91 if err != nil { 92 return 0, 0, E.Cause(err, "read netlink response") 93 } 94 95 buffer.Truncate(n) 96 97 messages, err := syscall.ParseNetlinkMessage(buffer.Bytes()) 98 if err != nil { 99 return 0, 0, E.Cause(err, "parse netlink message") 100 } else if len(messages) == 0 { 101 return 0, 0, E.New("unexcepted netlink response") 102 } 103 104 message := messages[0] 105 if message.Header.Type&syscall.NLMSG_ERROR != 0 { 106 return 0, 0, E.New("netlink message: NLMSG_ERROR") 107 } 108 109 inode, uid = unpackSocketDiagResponse(&messages[0]) 110 return 111 } 112 113 func packSocketDiagRequest(family, protocol byte, source netip.AddrPort) []byte { 114 s := make([]byte, 16) 115 copy(s, source.Addr().AsSlice()) 116 117 buf := make([]byte, sizeOfSocketDiagRequest) 118 119 nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest) 120 nativeEndian.PutUint16(buf[4:6], socketDiagByFamily) 121 nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP) 122 nativeEndian.PutUint32(buf[8:12], 0) 123 nativeEndian.PutUint32(buf[12:16], 0) 124 125 buf[16] = family 126 buf[17] = protocol 127 buf[18] = 0 128 buf[19] = 0 129 nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF) 130 131 binary.BigEndian.PutUint16(buf[24:26], source.Port()) 132 binary.BigEndian.PutUint16(buf[26:28], 0) 133 134 copy(buf[28:44], s) 135 copy(buf[44:60], net.IPv6zero) 136 137 nativeEndian.PutUint32(buf[60:64], 0) 138 nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF) 139 140 return buf 141 } 142 143 func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid uint32) { 144 if len(msg.Data) < 72 { 145 return 0, 0 146 } 147 148 data := msg.Data 149 150 uid = nativeEndian.Uint32(data[64:68]) 151 inode = nativeEndian.Uint32(data[68:72]) 152 153 return 154 } 155 156 func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { 157 files, err := os.ReadDir(pathProc) 158 if err != nil { 159 return "", err 160 } 161 162 buffer := make([]byte, syscall.PathMax) 163 socket := []byte(fmt.Sprintf("socket:[%d]", inode)) 164 165 for _, f := range files { 166 if !f.IsDir() || !isPid(f.Name()) { 167 continue 168 } 169 170 info, err := f.Info() 171 if err != nil { 172 return "", err 173 } 174 if info.Sys().(*syscall.Stat_t).Uid != uid { 175 continue 176 } 177 178 processPath := path.Join(pathProc, f.Name()) 179 fdPath := path.Join(processPath, "fd") 180 181 fds, err := os.ReadDir(fdPath) 182 if err != nil { 183 continue 184 } 185 186 for _, fd := range fds { 187 n, err := syscall.Readlink(path.Join(fdPath, fd.Name()), buffer) 188 if err != nil { 189 continue 190 } 191 192 if bytes.Equal(buffer[:n], socket) { 193 return os.Readlink(path.Join(processPath, "exe")) 194 } 195 } 196 } 197 198 return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) 199 } 200 201 func isPid(s string) bool { 202 return strings.IndexFunc(s, func(r rune) bool { 203 return !unicode.IsDigit(r) 204 }) == -1 205 }