searcher_darwin_shared.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //go:build darwin
  2. package process
  3. import (
  4. "encoding/binary"
  5. "net/netip"
  6. "os"
  7. "sync"
  8. "syscall"
  9. "time"
  10. "unsafe"
  11. "github.com/sagernet/sing-box/adapter"
  12. N "github.com/sagernet/sing/common/network"
  13. "golang.org/x/sys/unix"
  14. )
  15. const (
  16. darwinSnapshotTTL = 200 * time.Millisecond
  17. darwinXinpgenSize = 24
  18. darwinXsocketOffset = 104
  19. darwinXinpcbForeignPort = 16
  20. darwinXinpcbLocalPort = 18
  21. darwinXinpcbVFlag = 44
  22. darwinXinpcbForeignAddr = 48
  23. darwinXinpcbLocalAddr = 64
  24. darwinXinpcbIPv4Addr = 12
  25. darwinXsocketUID = 64
  26. darwinXsocketLastPID = 68
  27. darwinTCPExtraStructSize = 208
  28. )
  29. type darwinConnectionEntry struct {
  30. localAddr netip.Addr
  31. remoteAddr netip.Addr
  32. localPort uint16
  33. remotePort uint16
  34. pid uint32
  35. uid int32
  36. }
  37. type darwinConnectionMatchKind uint8
  38. const (
  39. darwinConnectionMatchExact darwinConnectionMatchKind = iota
  40. darwinConnectionMatchLocalFallback
  41. darwinConnectionMatchWildcardFallback
  42. )
  43. type darwinSnapshot struct {
  44. createdAt time.Time
  45. entries []darwinConnectionEntry
  46. }
  47. type darwinConnectionFinder struct {
  48. access sync.Mutex
  49. ttl time.Duration
  50. snapshots map[string]darwinSnapshot
  51. builder func(string) (darwinSnapshot, error)
  52. }
  53. var sharedDarwinConnectionFinder = newDarwinConnectionFinder(darwinSnapshotTTL)
  54. func newDarwinConnectionFinder(ttl time.Duration) *darwinConnectionFinder {
  55. return &darwinConnectionFinder{
  56. ttl: ttl,
  57. snapshots: make(map[string]darwinSnapshot),
  58. builder: buildDarwinSnapshot,
  59. }
  60. }
  61. func FindDarwinConnectionOwner(network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
  62. return sharedDarwinConnectionFinder.find(network, source, destination)
  63. }
  64. func (f *darwinConnectionFinder) find(network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
  65. networkName := N.NetworkName(network)
  66. source = normalizeDarwinAddrPort(source)
  67. destination = normalizeDarwinAddrPort(destination)
  68. var lastOwner *adapter.ConnectionOwner
  69. for attempt := 0; attempt < 2; attempt++ {
  70. snapshot, fromCache, err := f.loadSnapshot(networkName, attempt > 0)
  71. if err != nil {
  72. return nil, err
  73. }
  74. entry, matchKind, err := matchDarwinConnectionEntry(snapshot.entries, networkName, source, destination)
  75. if err != nil {
  76. if err == ErrNotFound && fromCache {
  77. continue
  78. }
  79. return nil, err
  80. }
  81. if fromCache && matchKind != darwinConnectionMatchExact {
  82. continue
  83. }
  84. owner := &adapter.ConnectionOwner{
  85. UserId: entry.uid,
  86. }
  87. lastOwner = owner
  88. if entry.pid == 0 {
  89. return owner, nil
  90. }
  91. processPath, err := getExecPathFromPID(entry.pid)
  92. if err == nil {
  93. owner.ProcessPath = processPath
  94. return owner, nil
  95. }
  96. if fromCache {
  97. continue
  98. }
  99. return owner, nil
  100. }
  101. if lastOwner != nil {
  102. return lastOwner, nil
  103. }
  104. return nil, ErrNotFound
  105. }
  106. func (f *darwinConnectionFinder) loadSnapshot(network string, forceRefresh bool) (darwinSnapshot, bool, error) {
  107. f.access.Lock()
  108. defer f.access.Unlock()
  109. if !forceRefresh {
  110. if snapshot, loaded := f.snapshots[network]; loaded && time.Since(snapshot.createdAt) < f.ttl {
  111. return snapshot, true, nil
  112. }
  113. }
  114. snapshot, err := f.builder(network)
  115. if err != nil {
  116. return darwinSnapshot{}, false, err
  117. }
  118. f.snapshots[network] = snapshot
  119. return snapshot, false, nil
  120. }
  121. func buildDarwinSnapshot(network string) (darwinSnapshot, error) {
  122. spath, itemSize, err := darwinSnapshotSettings(network)
  123. if err != nil {
  124. return darwinSnapshot{}, err
  125. }
  126. value, err := unix.SysctlRaw(spath)
  127. if err != nil {
  128. return darwinSnapshot{}, err
  129. }
  130. return darwinSnapshot{
  131. createdAt: time.Now(),
  132. entries: parseDarwinSnapshot(value, itemSize),
  133. }, nil
  134. }
  135. func darwinSnapshotSettings(network string) (string, int, error) {
  136. itemSize := structSize
  137. switch network {
  138. case N.NetworkTCP:
  139. return "net.inet.tcp.pcblist_n", itemSize + darwinTCPExtraStructSize, nil
  140. case N.NetworkUDP:
  141. return "net.inet.udp.pcblist_n", itemSize, nil
  142. default:
  143. return "", 0, os.ErrInvalid
  144. }
  145. }
  146. func parseDarwinSnapshot(buf []byte, itemSize int) []darwinConnectionEntry {
  147. entries := make([]darwinConnectionEntry, 0, (len(buf)-darwinXinpgenSize)/itemSize)
  148. for i := darwinXinpgenSize; i+itemSize <= len(buf); i += itemSize {
  149. inp := i
  150. so := i + darwinXsocketOffset
  151. entry, ok := parseDarwinConnectionEntry(buf[inp:so], buf[so:so+structSize-darwinXsocketOffset])
  152. if ok {
  153. entries = append(entries, entry)
  154. }
  155. }
  156. return entries
  157. }
  158. func parseDarwinConnectionEntry(inp []byte, so []byte) (darwinConnectionEntry, bool) {
  159. if len(inp) < darwinXsocketOffset || len(so) < structSize-darwinXsocketOffset {
  160. return darwinConnectionEntry{}, false
  161. }
  162. entry := darwinConnectionEntry{
  163. remotePort: binary.BigEndian.Uint16(inp[darwinXinpcbForeignPort : darwinXinpcbForeignPort+2]),
  164. localPort: binary.BigEndian.Uint16(inp[darwinXinpcbLocalPort : darwinXinpcbLocalPort+2]),
  165. pid: binary.NativeEndian.Uint32(so[darwinXsocketLastPID : darwinXsocketLastPID+4]),
  166. uid: int32(binary.NativeEndian.Uint32(so[darwinXsocketUID : darwinXsocketUID+4])),
  167. }
  168. flag := inp[darwinXinpcbVFlag]
  169. switch {
  170. case flag&0x1 != 0:
  171. entry.remoteAddr = netip.AddrFrom4([4]byte(inp[darwinXinpcbForeignAddr+darwinXinpcbIPv4Addr : darwinXinpcbForeignAddr+darwinXinpcbIPv4Addr+4]))
  172. entry.localAddr = netip.AddrFrom4([4]byte(inp[darwinXinpcbLocalAddr+darwinXinpcbIPv4Addr : darwinXinpcbLocalAddr+darwinXinpcbIPv4Addr+4]))
  173. return entry, true
  174. case flag&0x2 != 0:
  175. entry.remoteAddr = netip.AddrFrom16([16]byte(inp[darwinXinpcbForeignAddr : darwinXinpcbForeignAddr+16]))
  176. entry.localAddr = netip.AddrFrom16([16]byte(inp[darwinXinpcbLocalAddr : darwinXinpcbLocalAddr+16]))
  177. return entry, true
  178. default:
  179. return darwinConnectionEntry{}, false
  180. }
  181. }
  182. func matchDarwinConnectionEntry(entries []darwinConnectionEntry, network string, source netip.AddrPort, destination netip.AddrPort) (darwinConnectionEntry, darwinConnectionMatchKind, error) {
  183. sourceAddr := source.Addr()
  184. if !sourceAddr.IsValid() {
  185. return darwinConnectionEntry{}, darwinConnectionMatchExact, os.ErrInvalid
  186. }
  187. var localFallback darwinConnectionEntry
  188. var hasLocalFallback bool
  189. var wildcardFallback darwinConnectionEntry
  190. var hasWildcardFallback bool
  191. for _, entry := range entries {
  192. if entry.localPort != source.Port() || sourceAddr.BitLen() != entry.localAddr.BitLen() {
  193. continue
  194. }
  195. if entry.localAddr == sourceAddr && destination.IsValid() && entry.remotePort == destination.Port() && entry.remoteAddr == destination.Addr() {
  196. return entry, darwinConnectionMatchExact, nil
  197. }
  198. if !destination.IsValid() && entry.localAddr == sourceAddr {
  199. return entry, darwinConnectionMatchExact, nil
  200. }
  201. if network != N.NetworkUDP {
  202. continue
  203. }
  204. if !hasLocalFallback && entry.localAddr == sourceAddr {
  205. hasLocalFallback = true
  206. localFallback = entry
  207. }
  208. if !hasWildcardFallback && entry.localAddr.IsUnspecified() {
  209. hasWildcardFallback = true
  210. wildcardFallback = entry
  211. }
  212. }
  213. if hasLocalFallback {
  214. return localFallback, darwinConnectionMatchLocalFallback, nil
  215. }
  216. if hasWildcardFallback {
  217. return wildcardFallback, darwinConnectionMatchWildcardFallback, nil
  218. }
  219. return darwinConnectionEntry{}, darwinConnectionMatchExact, ErrNotFound
  220. }
  221. func normalizeDarwinAddrPort(addrPort netip.AddrPort) netip.AddrPort {
  222. if !addrPort.IsValid() {
  223. return addrPort
  224. }
  225. return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port())
  226. }
  227. func getExecPathFromPID(pid uint32) (string, error) {
  228. const (
  229. procpidpathinfo = 0xb
  230. procpidpathinfosize = 1024
  231. proccallnumpidinfo = 0x2
  232. )
  233. buf := make([]byte, procpidpathinfosize)
  234. _, _, errno := syscall.Syscall6(
  235. syscall.SYS_PROC_INFO,
  236. proccallnumpidinfo,
  237. uintptr(pid),
  238. procpidpathinfo,
  239. 0,
  240. uintptr(unsafe.Pointer(&buf[0])),
  241. procpidpathinfosize)
  242. if errno != 0 {
  243. return "", errno
  244. }
  245. return unix.ByteSliceToString(buf), nil
  246. }