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