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

     1  //go:build aix
     2  // +build aix
     3  
     4  package net
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os/exec"
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  
    15  	"github.com/gofiber/fiber/v2/internal/gopsutil/common"
    16  )
    17  
    18  func parseNetstatI(output string) ([]IOCountersStat, error) {
    19  	lines := strings.Split(string(output), "\n")
    20  	ret := make([]IOCountersStat, 0, len(lines)-1)
    21  	exists := make([]string, 0, len(ret))
    22  
    23  	// Check first line is header
    24  	if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" {
    25  		return nil, fmt.Errorf("not a 'netstat -i' output")
    26  	}
    27  
    28  	for _, line := range lines[1:] {
    29  		values := strings.Fields(line)
    30  		if len(values) < 1 || values[0] == "Name" {
    31  			continue
    32  		}
    33  		if common.StringsHas(exists, values[0]) {
    34  			// skip if already get
    35  			continue
    36  		}
    37  		exists = append(exists, values[0])
    38  
    39  		if len(values) < 9 {
    40  			continue
    41  		}
    42  
    43  		base := 1
    44  		// sometimes Address is omitted
    45  		if len(values) < 10 {
    46  			base = 0
    47  		}
    48  
    49  		parsed := make([]uint64, 0, 5)
    50  		vv := []string{
    51  			values[base+3], // Ipkts == PacketsRecv
    52  			values[base+4], // Ierrs == Errin
    53  			values[base+5], // Opkts == PacketsSent
    54  			values[base+6], // Oerrs == Errout
    55  			values[base+8], // Drops == Dropout
    56  		}
    57  
    58  		for _, target := range vv {
    59  			if target == "-" {
    60  				parsed = append(parsed, 0)
    61  				continue
    62  			}
    63  
    64  			t, err := strconv.ParseUint(target, 10, 64)
    65  			if err != nil {
    66  				return nil, err
    67  			}
    68  			parsed = append(parsed, t)
    69  		}
    70  
    71  		n := IOCountersStat{
    72  			Name:        values[0],
    73  			PacketsRecv: parsed[0],
    74  			Errin:       parsed[1],
    75  			PacketsSent: parsed[2],
    76  			Errout:      parsed[3],
    77  			Dropout:     parsed[4],
    78  		}
    79  		ret = append(ret, n)
    80  	}
    81  	return ret, nil
    82  }
    83  
    84  func IOCounters(pernic bool) ([]IOCountersStat, error) {
    85  	return IOCountersWithContext(context.Background(), pernic)
    86  }
    87  
    88  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
    89  	netstat, err := exec.LookPath("netstat")
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	out, err := invoke.CommandWithContext(ctx, netstat, "-idn")
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	iocounters, err := parseNetstatI(string(out))
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	if pernic == false {
   103  		return getIOCountersAll(iocounters)
   104  	}
   105  	return iocounters, nil
   106  
   107  }
   108  
   109  // NetIOCountersByFile is an method which is added just a compatibility for linux.
   110  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
   111  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
   112  }
   113  
   114  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
   115  	return IOCounters(pernic)
   116  }
   117  
   118  func FilterCounters() ([]FilterStat, error) {
   119  	return FilterCountersWithContext(context.Background())
   120  }
   121  
   122  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   123  	return nil, common.ErrNotImplementedError
   124  }
   125  
   126  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   127  	return ConntrackStatsWithContext(context.Background(), percpu)
   128  }
   129  
   130  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   131  	return nil, common.ErrNotImplementedError
   132  }
   133  
   134  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   135  	return ProtoCountersWithContext(context.Background(), protocols)
   136  }
   137  
   138  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   139  	return nil, common.ErrNotImplementedError
   140  }
   141  
   142  func parseNetstatNetLine(line string) (ConnectionStat, error) {
   143  	f := strings.Fields(line)
   144  	if len(f) < 5 {
   145  		return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
   146  	}
   147  
   148  	var netType, netFamily uint32
   149  	switch f[0] {
   150  	case "tcp", "tcp4":
   151  		netType = syscall.SOCK_STREAM
   152  		netFamily = syscall.AF_INET
   153  	case "udp", "udp4":
   154  		netType = syscall.SOCK_DGRAM
   155  		netFamily = syscall.AF_INET
   156  	case "tcp6":
   157  		netType = syscall.SOCK_STREAM
   158  		netFamily = syscall.AF_INET6
   159  	case "udp6":
   160  		netType = syscall.SOCK_DGRAM
   161  		netFamily = syscall.AF_INET6
   162  	default:
   163  		return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
   164  	}
   165  
   166  	laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
   167  	if err != nil {
   168  		return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
   169  	}
   170  
   171  	n := ConnectionStat{
   172  		Fd:     uint32(0), // not supported
   173  		Family: uint32(netFamily),
   174  		Type:   uint32(netType),
   175  		Laddr:  laddr,
   176  		Raddr:  raddr,
   177  		Pid:    int32(0), // not supported
   178  	}
   179  	if len(f) == 6 {
   180  		n.Status = f[5]
   181  	}
   182  
   183  	return n, nil
   184  }
   185  
   186  var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
   187  
   188  // This function only works for netstat returning addresses with a "."
   189  // before the port (0.0.0.0.22 instead of 0.0.0.0:22).
   190  func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
   191  	parse := func(l string) (Addr, error) {
   192  		matches := portMatch.FindStringSubmatch(l)
   193  		if matches == nil {
   194  			return Addr{}, fmt.Errorf("wrong addr, %s", l)
   195  		}
   196  		host := matches[1]
   197  		port := matches[2]
   198  		if host == "*" {
   199  			switch family {
   200  			case syscall.AF_INET:
   201  				host = "0.0.0.0"
   202  			case syscall.AF_INET6:
   203  				host = "::"
   204  			default:
   205  				return Addr{}, fmt.Errorf("unknown family, %d", family)
   206  			}
   207  		}
   208  		lport, err := strconv.Atoi(port)
   209  		if err != nil {
   210  			return Addr{}, err
   211  		}
   212  		return Addr{IP: host, Port: uint32(lport)}, nil
   213  	}
   214  
   215  	laddr, err = parse(local)
   216  	if remote != "*.*" { // remote addr exists
   217  		raddr, err = parse(remote)
   218  		if err != nil {
   219  			return laddr, raddr, err
   220  		}
   221  	}
   222  
   223  	return laddr, raddr, err
   224  }
   225  
   226  func parseNetstatUnixLine(f []string) (ConnectionStat, error) {
   227  	if len(f) < 8 {
   228  		return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f))
   229  	}
   230  
   231  	var netType uint32
   232  
   233  	switch f[1] {
   234  	case "dgram":
   235  		netType = syscall.SOCK_DGRAM
   236  	case "stream":
   237  		netType = syscall.SOCK_STREAM
   238  	default:
   239  		return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1])
   240  	}
   241  
   242  	// Some Unix Socket don't have any address associated
   243  	addr := ""
   244  	if len(f) == 9 {
   245  		addr = f[8]
   246  	}
   247  
   248  	c := ConnectionStat{
   249  		Fd:     uint32(0), // not supported
   250  		Family: uint32(syscall.AF_UNIX),
   251  		Type:   uint32(netType),
   252  		Laddr: Addr{
   253  			IP: addr,
   254  		},
   255  		Status: "NONE",
   256  		Pid:    int32(0), // not supported
   257  	}
   258  
   259  	return c, nil
   260  }
   261  
   262  // Return true if proto is the corresponding to the kind parameter
   263  // Only for Inet lines
   264  func hasCorrectInetProto(kind, proto string) bool {
   265  	switch kind {
   266  	case "all", "inet":
   267  		return true
   268  	case "unix":
   269  		return false
   270  	case "inet4":
   271  		return !strings.HasSuffix(proto, "6")
   272  	case "inet6":
   273  		return strings.HasSuffix(proto, "6")
   274  	case "tcp":
   275  		return proto == "tcp" || proto == "tcp4" || proto == "tcp6"
   276  	case "tcp4":
   277  		return proto == "tcp" || proto == "tcp4"
   278  	case "tcp6":
   279  		return proto == "tcp6"
   280  	case "udp":
   281  		return proto == "udp" || proto == "udp4" || proto == "udp6"
   282  	case "udp4":
   283  		return proto == "udp" || proto == "udp4"
   284  	case "udp6":
   285  		return proto == "udp6"
   286  	}
   287  	return false
   288  }
   289  
   290  func parseNetstatA(output string, kind string) ([]ConnectionStat, error) {
   291  	var ret []ConnectionStat
   292  	lines := strings.Split(string(output), "\n")
   293  
   294  	for _, line := range lines {
   295  		fields := strings.Fields(line)
   296  		if len(fields) < 1 {
   297  			continue
   298  		}
   299  
   300  		if strings.HasPrefix(fields[0], "f1") {
   301  			// Unix lines
   302  			if len(fields) < 2 {
   303  				// every unix connections have two lines
   304  				continue
   305  			}
   306  
   307  			c, err := parseNetstatUnixLine(fields)
   308  			if err != nil {
   309  				return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err)
   310  			}
   311  
   312  			ret = append(ret, c)
   313  
   314  		} else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") {
   315  			// Inet lines
   316  			if !hasCorrectInetProto(kind, fields[0]) {
   317  				continue
   318  			}
   319  
   320  			// On AIX, netstat display some connections with "*.*" as local addresses
   321  			// Skip them as they aren't real connections.
   322  			if fields[3] == "*.*" {
   323  				continue
   324  			}
   325  
   326  			c, err := parseNetstatNetLine(line)
   327  			if err != nil {
   328  				return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err)
   329  			}
   330  
   331  			ret = append(ret, c)
   332  		} else {
   333  			// Header lines
   334  			continue
   335  		}
   336  	}
   337  
   338  	return ret, nil
   339  
   340  }
   341  
   342  func Connections(kind string) ([]ConnectionStat, error) {
   343  	return ConnectionsWithContext(context.Background(), kind)
   344  }
   345  
   346  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   347  
   348  	args := []string{"-na"}
   349  	switch strings.ToLower(kind) {
   350  	default:
   351  		fallthrough
   352  	case "":
   353  		kind = "all"
   354  	case "all":
   355  		// nothing to add
   356  	case "inet", "inet4", "inet6":
   357  		args = append(args, "-finet")
   358  	case "tcp", "tcp4", "tcp6":
   359  		args = append(args, "-finet")
   360  	case "udp", "udp4", "udp6":
   361  		args = append(args, "-finet")
   362  	case "unix":
   363  		args = append(args, "-funix")
   364  	}
   365  
   366  	netstat, err := exec.LookPath("netstat")
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	out, err := invoke.CommandWithContext(ctx, netstat, args...)
   371  
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	ret, err := parseNetstatA(string(out), kind)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  
   381  	return ret, nil
   382  
   383  }
   384  
   385  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
   386  	return ConnectionsMaxWithContext(context.Background(), kind, max)
   387  }
   388  
   389  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   390  	return []ConnectionStat{}, common.ErrNotImplementedError
   391  }
   392  
   393  // Return a list of network connections opened, omitting `Uids`.
   394  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   395  // removed from the API in the future.
   396  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   397  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   398  }
   399  
   400  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   401  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   402  }
   403  
   404  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   405  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   406  }
   407  
   408  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   409  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   410  }
   411  
   412  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   413  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   414  }
   415  
   416  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   417  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   418  }
   419  
   420  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   421  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
   422  }
   423  
   424  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   425  	return []ConnectionStat{}, common.ErrNotImplementedError
   426  }