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  }