github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/net/net_linux.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package net
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/hex"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"net"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"syscall"
    18  
    19  	"github.com/gofiber/fiber/v2/internal/gopsutil/common"
    20  )
    21  
    22  const ( // Conntrack Column numbers
    23  	CT_ENTRIES = iota
    24  	CT_SEARCHED
    25  	CT_FOUND
    26  	CT_NEW
    27  	CT_INVALID
    28  	CT_IGNORE
    29  	CT_DELETE
    30  	CT_DELETE_LIST
    31  	CT_INSERT
    32  	CT_INSERT_FAILED
    33  	CT_DROP
    34  	CT_EARLY_DROP
    35  	CT_ICMP_ERROR
    36  	CT_EXPECT_NEW
    37  	CT_EXPECT_CREATE
    38  	CT_EXPECT_DELETE
    39  	CT_SEARCH_RESTART
    40  )
    41  
    42  // NetIOCounters returnes network I/O statistics for every network
    43  // interface installed on the system.  If pernic argument is false,
    44  // return only sum of all information (which name is 'all'). If true,
    45  // every network interface installed on the system is returned
    46  // separately.
    47  func IOCounters(pernic bool) ([]IOCountersStat, error) {
    48  	return IOCountersWithContext(context.Background(), pernic)
    49  }
    50  
    51  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
    52  	filename := common.HostProc("net/dev")
    53  	return IOCountersByFileWithContext(ctx, pernic, filename)
    54  }
    55  
    56  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
    57  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
    58  }
    59  
    60  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
    61  	lines, err := common.ReadLines(filename)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	parts := make([]string, 2)
    67  
    68  	statlen := len(lines) - 1
    69  
    70  	ret := make([]IOCountersStat, 0, statlen)
    71  
    72  	for _, line := range lines[2:] {
    73  		separatorPos := strings.LastIndex(line, ":")
    74  		if separatorPos == -1 {
    75  			continue
    76  		}
    77  		parts[0] = line[0:separatorPos]
    78  		parts[1] = line[separatorPos+1:]
    79  
    80  		interfaceName := strings.TrimSpace(parts[0])
    81  		if interfaceName == "" {
    82  			continue
    83  		}
    84  
    85  		fields := strings.Fields(strings.TrimSpace(parts[1]))
    86  		bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
    87  		if err != nil {
    88  			return ret, err
    89  		}
    90  		packetsRecv, err := strconv.ParseUint(fields[1], 10, 64)
    91  		if err != nil {
    92  			return ret, err
    93  		}
    94  		errIn, err := strconv.ParseUint(fields[2], 10, 64)
    95  		if err != nil {
    96  			return ret, err
    97  		}
    98  		dropIn, err := strconv.ParseUint(fields[3], 10, 64)
    99  		if err != nil {
   100  			return ret, err
   101  		}
   102  		fifoIn, err := strconv.ParseUint(fields[4], 10, 64)
   103  		if err != nil {
   104  			return ret, err
   105  		}
   106  		bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
   107  		if err != nil {
   108  			return ret, err
   109  		}
   110  		packetsSent, err := strconv.ParseUint(fields[9], 10, 64)
   111  		if err != nil {
   112  			return ret, err
   113  		}
   114  		errOut, err := strconv.ParseUint(fields[10], 10, 64)
   115  		if err != nil {
   116  			return ret, err
   117  		}
   118  		dropOut, err := strconv.ParseUint(fields[11], 10, 64)
   119  		if err != nil {
   120  			return ret, err
   121  		}
   122  		fifoOut, err := strconv.ParseUint(fields[12], 10, 64)
   123  		if err != nil {
   124  			return ret, err
   125  		}
   126  
   127  		nic := IOCountersStat{
   128  			Name:        interfaceName,
   129  			BytesRecv:   bytesRecv,
   130  			PacketsRecv: packetsRecv,
   131  			Errin:       errIn,
   132  			Dropin:      dropIn,
   133  			Fifoin:      fifoIn,
   134  			BytesSent:   bytesSent,
   135  			PacketsSent: packetsSent,
   136  			Errout:      errOut,
   137  			Dropout:     dropOut,
   138  			Fifoout:     fifoOut,
   139  		}
   140  		ret = append(ret, nic)
   141  	}
   142  
   143  	if !pernic {
   144  		return getIOCountersAll(ret)
   145  	}
   146  
   147  	return ret, nil
   148  }
   149  
   150  var netProtocols = []string{
   151  	"ip",
   152  	"icmp",
   153  	"icmpmsg",
   154  	"tcp",
   155  	"udp",
   156  	"udplite",
   157  }
   158  
   159  // NetProtoCounters returns network statistics for the entire system
   160  // If protocols is empty then all protocols are returned, otherwise
   161  // just the protocols in the list are returned.
   162  // Available protocols:
   163  //
   164  //	ip,icmp,icmpmsg,tcp,udp,udplite
   165  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   166  	return ProtoCountersWithContext(context.Background(), protocols)
   167  }
   168  
   169  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   170  	if len(protocols) == 0 {
   171  		protocols = netProtocols
   172  	}
   173  
   174  	stats := make([]ProtoCountersStat, 0, len(protocols))
   175  	protos := make(map[string]bool, len(protocols))
   176  	for _, p := range protocols {
   177  		protos[p] = true
   178  	}
   179  
   180  	filename := common.HostProc("net/snmp")
   181  	lines, err := common.ReadLines(filename)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	linecount := len(lines)
   187  	for i := 0; i < linecount; i++ {
   188  		line := lines[i]
   189  		r := strings.IndexRune(line, ':')
   190  		if r == -1 {
   191  			return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
   192  		}
   193  		proto := strings.ToLower(line[:r])
   194  		if !protos[proto] {
   195  			// skip protocol and data line
   196  			i++
   197  			continue
   198  		}
   199  
   200  		// Read header line
   201  		statNames := strings.Split(line[r+2:], " ")
   202  
   203  		// Read data line
   204  		i++
   205  		statValues := strings.Split(lines[i][r+2:], " ")
   206  		if len(statNames) != len(statValues) {
   207  			return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
   208  		}
   209  		stat := ProtoCountersStat{
   210  			Protocol: proto,
   211  			Stats:    make(map[string]int64, len(statNames)),
   212  		}
   213  		for j := range statNames {
   214  			value, err := strconv.ParseInt(statValues[j], 10, 64)
   215  			if err != nil {
   216  				return nil, err
   217  			}
   218  			stat.Stats[statNames[j]] = value
   219  		}
   220  		stats = append(stats, stat)
   221  	}
   222  	return stats, nil
   223  }
   224  
   225  // NetFilterCounters returns iptables conntrack statistics
   226  // the currently in use conntrack count and the max.
   227  // If the file does not exist or is invalid it will return nil.
   228  func FilterCounters() ([]FilterStat, error) {
   229  	return FilterCountersWithContext(context.Background())
   230  }
   231  
   232  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   233  	countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
   234  	maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
   235  
   236  	count, err := common.ReadInts(countfile)
   237  
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	stats := make([]FilterStat, 0, 1)
   242  
   243  	max, err := common.ReadInts(maxfile)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	payload := FilterStat{
   249  		ConnTrackCount: count[0],
   250  		ConnTrackMax:   max[0],
   251  	}
   252  
   253  	stats = append(stats, payload)
   254  	return stats, nil
   255  }
   256  
   257  // ConntrackStats returns more detailed info about the conntrack table
   258  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   259  	return ConntrackStatsWithContext(context.Background(), percpu)
   260  }
   261  
   262  // ConntrackStatsWithContext returns more detailed info about the conntrack table
   263  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   264  	return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu)
   265  }
   266  
   267  // conntrackStatsFromFile returns more detailed info about the conntrack table
   268  // from `filename`
   269  // If 'percpu' is false, the result will contain exactly one item with totals/summary
   270  func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) {
   271  	lines, err := common.ReadLines(filename)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  
   276  	statlist := NewConntrackStatList()
   277  
   278  	for _, line := range lines {
   279  		fields := strings.Fields(line)
   280  		if len(fields) == 17 && fields[0] != "entries" {
   281  			statlist.Append(NewConntrackStat(
   282  				common.HexToUint32(fields[CT_ENTRIES]),
   283  				common.HexToUint32(fields[CT_SEARCHED]),
   284  				common.HexToUint32(fields[CT_FOUND]),
   285  				common.HexToUint32(fields[CT_NEW]),
   286  				common.HexToUint32(fields[CT_INVALID]),
   287  				common.HexToUint32(fields[CT_IGNORE]),
   288  				common.HexToUint32(fields[CT_DELETE]),
   289  				common.HexToUint32(fields[CT_DELETE_LIST]),
   290  				common.HexToUint32(fields[CT_INSERT]),
   291  				common.HexToUint32(fields[CT_INSERT_FAILED]),
   292  				common.HexToUint32(fields[CT_DROP]),
   293  				common.HexToUint32(fields[CT_EARLY_DROP]),
   294  				common.HexToUint32(fields[CT_ICMP_ERROR]),
   295  				common.HexToUint32(fields[CT_EXPECT_NEW]),
   296  				common.HexToUint32(fields[CT_EXPECT_CREATE]),
   297  				common.HexToUint32(fields[CT_EXPECT_DELETE]),
   298  				common.HexToUint32(fields[CT_SEARCH_RESTART]),
   299  			))
   300  		}
   301  	}
   302  
   303  	if percpu {
   304  		return statlist.Items(), nil
   305  	}
   306  	return statlist.Summary(), nil
   307  }
   308  
   309  // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
   310  var TCPStatuses = map[string]string{
   311  	"01": "ESTABLISHED",
   312  	"02": "SYN_SENT",
   313  	"03": "SYN_RECV",
   314  	"04": "FIN_WAIT1",
   315  	"05": "FIN_WAIT2",
   316  	"06": "TIME_WAIT",
   317  	"07": "CLOSE",
   318  	"08": "CLOSE_WAIT",
   319  	"09": "LAST_ACK",
   320  	"0A": "LISTEN",
   321  	"0B": "CLOSING",
   322  }
   323  
   324  type netConnectionKindType struct {
   325  	family   uint32
   326  	sockType uint32
   327  	filename string
   328  }
   329  
   330  var kindTCP4 = netConnectionKindType{
   331  	family:   syscall.AF_INET,
   332  	sockType: syscall.SOCK_STREAM,
   333  	filename: "tcp",
   334  }
   335  var kindTCP6 = netConnectionKindType{
   336  	family:   syscall.AF_INET6,
   337  	sockType: syscall.SOCK_STREAM,
   338  	filename: "tcp6",
   339  }
   340  var kindUDP4 = netConnectionKindType{
   341  	family:   syscall.AF_INET,
   342  	sockType: syscall.SOCK_DGRAM,
   343  	filename: "udp",
   344  }
   345  var kindUDP6 = netConnectionKindType{
   346  	family:   syscall.AF_INET6,
   347  	sockType: syscall.SOCK_DGRAM,
   348  	filename: "udp6",
   349  }
   350  var kindUNIX = netConnectionKindType{
   351  	family:   syscall.AF_UNIX,
   352  	filename: "unix",
   353  }
   354  
   355  var netConnectionKindMap = map[string][]netConnectionKindType{
   356  	"all":   {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX},
   357  	"tcp":   {kindTCP4, kindTCP6},
   358  	"tcp4":  {kindTCP4},
   359  	"tcp6":  {kindTCP6},
   360  	"udp":   {kindUDP4, kindUDP6},
   361  	"udp4":  {kindUDP4},
   362  	"udp6":  {kindUDP6},
   363  	"unix":  {kindUNIX},
   364  	"inet":  {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
   365  	"inet4": {kindTCP4, kindUDP4},
   366  	"inet6": {kindTCP6, kindUDP6},
   367  }
   368  
   369  type inodeMap struct {
   370  	pid int32
   371  	fd  uint32
   372  }
   373  
   374  type connTmp struct {
   375  	fd       uint32
   376  	family   uint32
   377  	sockType uint32
   378  	laddr    Addr
   379  	raddr    Addr
   380  	status   string
   381  	pid      int32
   382  	boundPid int32
   383  	path     string
   384  }
   385  
   386  // Return a list of network connections opened.
   387  func Connections(kind string) ([]ConnectionStat, error) {
   388  	return ConnectionsWithContext(context.Background(), kind)
   389  }
   390  
   391  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   392  	return ConnectionsPid(kind, 0)
   393  }
   394  
   395  // Return a list of network connections opened returning at most `max`
   396  // connections for each running process.
   397  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
   398  	return ConnectionsMaxWithContext(context.Background(), kind, max)
   399  }
   400  
   401  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   402  	return ConnectionsPidMax(kind, 0, max)
   403  }
   404  
   405  // Return a list of network connections opened, omitting `Uids`.
   406  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   407  // removed from the API in the future.
   408  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   409  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   410  }
   411  
   412  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   413  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   414  }
   415  
   416  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   417  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   418  }
   419  
   420  // Return a list of network connections opened by a process.
   421  func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
   422  	return ConnectionsPidWithContext(context.Background(), kind, pid)
   423  }
   424  
   425  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   426  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   427  }
   428  
   429  func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   430  	return ConnectionsPidMaxWithContext(ctx, kind, pid, 0)
   431  }
   432  
   433  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   434  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   435  }
   436  
   437  // Return up to `max` network connections opened by a process.
   438  func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
   439  	return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
   440  }
   441  
   442  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   443  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   444  }
   445  
   446  func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   447  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false)
   448  }
   449  
   450  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   451  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true)
   452  }
   453  
   454  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) {
   455  	tmap, ok := netConnectionKindMap[kind]
   456  	if !ok {
   457  		return nil, fmt.Errorf("invalid kind, %s", kind)
   458  	}
   459  	root := common.HostProc()
   460  	var err error
   461  	var inodes map[string][]inodeMap
   462  	if pid == 0 {
   463  		inodes, err = getProcInodesAll(root, max)
   464  	} else {
   465  		inodes, err = getProcInodes(root, pid, max)
   466  		if len(inodes) == 0 {
   467  			// no connection for the pid
   468  			return []ConnectionStat{}, nil
   469  		}
   470  	}
   471  	if err != nil {
   472  		return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err)
   473  	}
   474  	return statsFromInodes(root, pid, tmap, inodes, skipUids)
   475  }
   476  
   477  func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
   478  	dupCheckMap := make(map[string]struct{})
   479  	var ret []ConnectionStat
   480  
   481  	var err error
   482  	for _, t := range tmap {
   483  		var path string
   484  		var connKey string
   485  		var ls []connTmp
   486  		if pid == 0 {
   487  			path = fmt.Sprintf("%s/net/%s", root, t.filename)
   488  		} else {
   489  			path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
   490  		}
   491  		switch t.family {
   492  		case syscall.AF_INET, syscall.AF_INET6:
   493  			ls, err = processInet(path, t, inodes, pid)
   494  		case syscall.AF_UNIX:
   495  			ls, err = processUnix(path, t, inodes, pid)
   496  		}
   497  		if err != nil {
   498  			return nil, err
   499  		}
   500  		for _, c := range ls {
   501  			// Build TCP key to id the connection uniquely
   502  			// socket type, src ip, src port, dst ip, dst port and state should be enough
   503  			// to prevent duplications.
   504  			connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status)
   505  			if _, ok := dupCheckMap[connKey]; ok {
   506  				continue
   507  			}
   508  
   509  			conn := ConnectionStat{
   510  				Fd:     c.fd,
   511  				Family: c.family,
   512  				Type:   c.sockType,
   513  				Laddr:  c.laddr,
   514  				Raddr:  c.raddr,
   515  				Status: c.status,
   516  				Pid:    c.pid,
   517  			}
   518  			if c.pid == 0 {
   519  				conn.Pid = c.boundPid
   520  			} else {
   521  				conn.Pid = c.pid
   522  			}
   523  
   524  			if !skipUids {
   525  				// fetch process owner Real, effective, saved set, and filesystem UIDs
   526  				proc := process{Pid: conn.Pid}
   527  				conn.Uids, _ = proc.getUids()
   528  			}
   529  
   530  			ret = append(ret, conn)
   531  			dupCheckMap[connKey] = struct{}{}
   532  		}
   533  
   534  	}
   535  
   536  	return ret, nil
   537  }
   538  
   539  // getProcInodes returnes fd of the pid.
   540  func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
   541  	ret := make(map[string][]inodeMap)
   542  
   543  	dir := fmt.Sprintf("%s/%d/fd", root, pid)
   544  	f, err := os.Open(dir)
   545  	if err != nil {
   546  		return ret, err
   547  	}
   548  	defer f.Close()
   549  	files, err := f.Readdir(max)
   550  	if err != nil {
   551  		return ret, err
   552  	}
   553  	for _, fd := range files {
   554  		inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
   555  
   556  		inode, err := os.Readlink(inodePath)
   557  		if err != nil {
   558  			continue
   559  		}
   560  		if !strings.HasPrefix(inode, "socket:[") {
   561  			continue
   562  		}
   563  		// the process is using a socket
   564  		l := len(inode)
   565  		inode = inode[8 : l-1]
   566  		_, ok := ret[inode]
   567  		if !ok {
   568  			ret[inode] = make([]inodeMap, 0)
   569  		}
   570  		fd, err := strconv.Atoi(fd.Name())
   571  		if err != nil {
   572  			continue
   573  		}
   574  
   575  		i := inodeMap{
   576  			pid: pid,
   577  			fd:  uint32(fd),
   578  		}
   579  		ret[inode] = append(ret[inode], i)
   580  	}
   581  	return ret, nil
   582  }
   583  
   584  // Pids retunres all pids.
   585  // Note: this is a copy of process_linux.Pids()
   586  // FIXME: Import process occures import cycle.
   587  // move to common made other platform breaking. Need consider.
   588  func Pids() ([]int32, error) {
   589  	return PidsWithContext(context.Background())
   590  }
   591  
   592  func PidsWithContext(ctx context.Context) ([]int32, error) {
   593  	var ret []int32
   594  
   595  	d, err := os.Open(common.HostProc())
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  	defer d.Close()
   600  
   601  	fnames, err := d.Readdirnames(-1)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  	for _, fname := range fnames {
   606  		pid, err := strconv.ParseInt(fname, 10, 32)
   607  		if err != nil {
   608  			// if not numeric name, just skip
   609  			continue
   610  		}
   611  		ret = append(ret, int32(pid))
   612  	}
   613  
   614  	return ret, nil
   615  }
   616  
   617  // Note: the following is based off process_linux structs and methods
   618  // we need these to fetch the owner of a process ID
   619  // FIXME: Import process occures import cycle.
   620  // see remarks on pids()
   621  type process struct {
   622  	Pid  int32 `json:"pid"`
   623  	uids []int32
   624  }
   625  
   626  // Uids returns user ids of the process as a slice of the int
   627  func (p *process) getUids() ([]int32, error) {
   628  	err := p.fillFromStatus()
   629  	if err != nil {
   630  		return []int32{}, err
   631  	}
   632  	return p.uids, nil
   633  }
   634  
   635  // Get status from /proc/(pid)/status
   636  func (p *process) fillFromStatus() error {
   637  	pid := p.Pid
   638  	statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
   639  	contents, err := os.ReadFile(statPath)
   640  	if err != nil {
   641  		return err
   642  	}
   643  	lines := strings.Split(string(contents), "\n")
   644  	for _, line := range lines {
   645  		tabParts := strings.SplitN(line, "\t", 2)
   646  		if len(tabParts) < 2 {
   647  			continue
   648  		}
   649  		value := tabParts[1]
   650  		switch strings.TrimRight(tabParts[0], ":") {
   651  		case "Uid":
   652  			p.uids = make([]int32, 0, 4)
   653  			for _, i := range strings.Split(value, "\t") {
   654  				v, err := strconv.ParseInt(i, 10, 32)
   655  				if err != nil {
   656  					return err
   657  				}
   658  				p.uids = append(p.uids, int32(v))
   659  			}
   660  		}
   661  	}
   662  	return nil
   663  }
   664  
   665  func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
   666  	pids, err := Pids()
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  	ret := make(map[string][]inodeMap)
   671  
   672  	for _, pid := range pids {
   673  		t, err := getProcInodes(root, pid, max)
   674  		if err != nil {
   675  			// skip if permission error or no longer exists
   676  			if os.IsPermission(err) || os.IsNotExist(err) || err == io.EOF {
   677  				continue
   678  			}
   679  			return ret, err
   680  		}
   681  		if len(t) == 0 {
   682  			continue
   683  		}
   684  		// TODO: update ret.
   685  		ret = updateMap(ret, t)
   686  	}
   687  	return ret, nil
   688  }
   689  
   690  // decodeAddress decode addresse represents addr in proc/net/*
   691  // ex:
   692  // "0500000A:0016" -> "10.0.0.5", 22
   693  // "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
   694  func decodeAddress(family uint32, src string) (Addr, error) {
   695  	t := strings.Split(src, ":")
   696  	if len(t) != 2 {
   697  		return Addr{}, fmt.Errorf("does not contain port, %s", src)
   698  	}
   699  	addr := t[0]
   700  	port, err := strconv.ParseUint(t[1], 16, 16)
   701  	if err != nil {
   702  		return Addr{}, fmt.Errorf("invalid port, %s", src)
   703  	}
   704  	decoded, err := hex.DecodeString(addr)
   705  	if err != nil {
   706  		return Addr{}, fmt.Errorf("decode error, %s", err)
   707  	}
   708  	var ip net.IP
   709  	// Assumes this is little_endian
   710  	if family == syscall.AF_INET {
   711  		ip = net.IP(Reverse(decoded))
   712  	} else { // IPv6
   713  		ip, err = parseIPv6HexString(decoded)
   714  		if err != nil {
   715  			return Addr{}, err
   716  		}
   717  	}
   718  	return Addr{
   719  		IP:   ip.String(),
   720  		Port: uint32(port),
   721  	}, nil
   722  }
   723  
   724  // Reverse reverses array of bytes.
   725  func Reverse(s []byte) []byte {
   726  	return ReverseWithContext(context.Background(), s)
   727  }
   728  
   729  func ReverseWithContext(ctx context.Context, s []byte) []byte {
   730  	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
   731  		s[i], s[j] = s[j], s[i]
   732  	}
   733  	return s
   734  }
   735  
   736  // parseIPv6HexString parse array of bytes to IPv6 string
   737  func parseIPv6HexString(src []byte) (net.IP, error) {
   738  	if len(src) != 16 {
   739  		return nil, fmt.Errorf("invalid IPv6 string")
   740  	}
   741  
   742  	buf := make([]byte, 0, 16)
   743  	for i := 0; i < len(src); i += 4 {
   744  		r := Reverse(src[i : i+4])
   745  		buf = append(buf, r...)
   746  	}
   747  	return net.IP(buf), nil
   748  }
   749  
   750  func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   751  
   752  	if strings.HasSuffix(file, "6") && !common.PathExists(file) {
   753  		// IPv6 not supported, return empty.
   754  		return []connTmp{}, nil
   755  	}
   756  
   757  	// Read the contents of the /proc file with a single read sys call.
   758  	// This minimizes duplicates in the returned connections
   759  	// For more info:
   760  	// https://github.com/shirou/gopsutil/pull/361
   761  	contents, err := os.ReadFile(file)
   762  	if err != nil {
   763  		return nil, err
   764  	}
   765  
   766  	lines := bytes.Split(contents, []byte("\n"))
   767  
   768  	var ret []connTmp
   769  	// skip first line
   770  	for _, line := range lines[1:] {
   771  		l := strings.Fields(string(line))
   772  		if len(l) < 10 {
   773  			continue
   774  		}
   775  		laddr := l[1]
   776  		raddr := l[2]
   777  		status := l[3]
   778  		inode := l[9]
   779  		pid := int32(0)
   780  		fd := uint32(0)
   781  		i, exists := inodes[inode]
   782  		if exists {
   783  			pid = i[0].pid
   784  			fd = i[0].fd
   785  		}
   786  		if filterPid > 0 && filterPid != pid {
   787  			continue
   788  		}
   789  		if kind.sockType == syscall.SOCK_STREAM {
   790  			status = TCPStatuses[status]
   791  		} else {
   792  			status = "NONE"
   793  		}
   794  		la, err := decodeAddress(kind.family, laddr)
   795  		if err != nil {
   796  			continue
   797  		}
   798  		ra, err := decodeAddress(kind.family, raddr)
   799  		if err != nil {
   800  			continue
   801  		}
   802  
   803  		ret = append(ret, connTmp{
   804  			fd:       fd,
   805  			family:   kind.family,
   806  			sockType: kind.sockType,
   807  			laddr:    la,
   808  			raddr:    ra,
   809  			status:   status,
   810  			pid:      pid,
   811  		})
   812  	}
   813  
   814  	return ret, nil
   815  }
   816  
   817  func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   818  	// Read the contents of the /proc file with a single read sys call.
   819  	// This minimizes duplicates in the returned connections
   820  	// For more info:
   821  	// https://github.com/shirou/gopsutil/pull/361
   822  	contents, err := os.ReadFile(file)
   823  	if err != nil {
   824  		return nil, err
   825  	}
   826  
   827  	lines := bytes.Split(contents, []byte("\n"))
   828  
   829  	var ret []connTmp
   830  	// skip first line
   831  	for _, line := range lines[1:] {
   832  		tokens := strings.Fields(string(line))
   833  		if len(tokens) < 6 {
   834  			continue
   835  		}
   836  		st, err := strconv.Atoi(tokens[4])
   837  		if err != nil {
   838  			return nil, err
   839  		}
   840  
   841  		inode := tokens[6]
   842  
   843  		var pairs []inodeMap
   844  		pairs, exists := inodes[inode]
   845  		if !exists {
   846  			pairs = []inodeMap{
   847  				{},
   848  			}
   849  		}
   850  		for _, pair := range pairs {
   851  			if filterPid > 0 && filterPid != pair.pid {
   852  				continue
   853  			}
   854  			var path string
   855  			if len(tokens) == 8 {
   856  				path = tokens[len(tokens)-1]
   857  			}
   858  			ret = append(ret, connTmp{
   859  				fd:       pair.fd,
   860  				family:   kind.family,
   861  				sockType: uint32(st),
   862  				laddr: Addr{
   863  					IP: path,
   864  				},
   865  				pid:    pair.pid,
   866  				status: "NONE",
   867  				path:   path,
   868  			})
   869  		}
   870  	}
   871  
   872  	return ret, nil
   873  }
   874  
   875  func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap {
   876  	for key, value := range add {
   877  		a, exists := src[key]
   878  		if !exists {
   879  			src[key] = value
   880  			continue
   881  		}
   882  		src[key] = append(a, value...)
   883  	}
   884  	return src
   885  }