github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/internal/procfs/procfs.go (about) 1 package procfs 2 3 import ( 4 "bufio" 5 "encoding/binary" 6 "encoding/hex" 7 "fmt" 8 "net" 9 "net/netip" 10 "os" 11 "strconv" 12 "strings" 13 "unsafe" 14 15 N "github.com/sagernet/sing/common/network" 16 ) 17 18 var ( 19 netIndexOfLocal = -1 20 netIndexOfUid = -1 21 nativeEndian binary.ByteOrder 22 ) 23 24 func init() { 25 var x uint32 = 0x01020304 26 if *(*byte)(unsafe.Pointer(&x)) == 0x01 { 27 nativeEndian = binary.BigEndian 28 } else { 29 nativeEndian = binary.LittleEndian 30 } 31 } 32 33 func ResolveSocketByProcSearch(network string, source, _ netip.AddrPort) int32 { 34 if netIndexOfLocal < 0 || netIndexOfUid < 0 { 35 return -1 36 } 37 38 path := "/proc/net/" 39 40 if network == N.NetworkTCP { 41 path += "tcp" 42 } else { 43 path += "udp" 44 } 45 46 if source.Addr().Is6() { 47 path += "6" 48 } 49 50 sIP := source.Addr().AsSlice() 51 if len(sIP) == 0 { 52 return -1 53 } 54 55 var bytes [2]byte 56 binary.BigEndian.PutUint16(bytes[:], source.Port()) 57 local := fmt.Sprintf("%s:%s", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:])) 58 59 file, err := os.Open(path) 60 if err != nil { 61 return -1 62 } 63 64 defer file.Close() 65 66 reader := bufio.NewReader(file) 67 68 for { 69 row, _, err := reader.ReadLine() 70 if err != nil { 71 return -1 72 } 73 74 fields := strings.Fields(string(row)) 75 76 if len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid { 77 continue 78 } 79 80 if strings.EqualFold(local, fields[netIndexOfLocal]) { 81 uid, err := strconv.Atoi(fields[netIndexOfUid]) 82 if err != nil { 83 return -1 84 } 85 86 return int32(uid) 87 } 88 } 89 } 90 91 func nativeEndianIP(ip net.IP) []byte { 92 result := make([]byte, len(ip)) 93 94 for i := 0; i < len(ip); i += 4 { 95 value := binary.BigEndian.Uint32(ip[i:]) 96 97 nativeEndian.PutUint32(result[i:], value) 98 } 99 100 return result 101 } 102 103 func init() { 104 file, err := os.Open("/proc/net/tcp") 105 if err != nil { 106 return 107 } 108 109 defer file.Close() 110 111 reader := bufio.NewReader(file) 112 113 header, _, err := reader.ReadLine() 114 if err != nil { 115 return 116 } 117 118 columns := strings.Fields(string(header)) 119 120 var txQueue, rxQueue, tr, tmWhen bool 121 122 for idx, col := range columns { 123 offset := 0 124 125 if txQueue && rxQueue { 126 offset-- 127 } 128 129 if tr && tmWhen { 130 offset-- 131 } 132 133 switch col { 134 case "tx_queue": 135 txQueue = true 136 case "rx_queue": 137 rxQueue = true 138 case "tr": 139 tr = true 140 case "tm->when": 141 tmWhen = true 142 case "local_address": 143 netIndexOfLocal = idx + offset 144 case "uid": 145 netIndexOfUid = idx + offset 146 } 147 } 148 }