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

     1  //go:build openbsd
     2  // +build openbsd
     3  
     4  package net
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"os/exec"
    11  	"regexp"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  
    16  	"github.com/gofiber/fiber/v2/internal/gopsutil/common"
    17  )
    18  
    19  var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
    20  
    21  func ParseNetstat(output string, mode string,
    22  	iocs map[string]IOCountersStat) error {
    23  	lines := strings.Split(output, "\n")
    24  
    25  	exists := make([]string, 0, len(lines)-1)
    26  
    27  	columns := 6
    28  	if mode == "ind" {
    29  		columns = 10
    30  	}
    31  	for _, line := range lines {
    32  		values := strings.Fields(line)
    33  		if len(values) < 1 || values[0] == "Name" {
    34  			continue
    35  		}
    36  		if common.StringsHas(exists, values[0]) {
    37  			// skip if already get
    38  			continue
    39  		}
    40  
    41  		if len(values) < columns {
    42  			continue
    43  		}
    44  		base := 1
    45  		// sometimes Address is omitted
    46  		if len(values) < columns {
    47  			base = 0
    48  		}
    49  
    50  		parsed := make([]uint64, 0, 8)
    51  		var vv []string
    52  		if mode == "inb" {
    53  			vv = []string{
    54  				values[base+3], // BytesRecv
    55  				values[base+4], // BytesSent
    56  			}
    57  		} else {
    58  			vv = []string{
    59  				values[base+3], // Ipkts
    60  				values[base+4], // Ierrs
    61  				values[base+5], // Opkts
    62  				values[base+6], // Oerrs
    63  				values[base+8], // Drops
    64  			}
    65  		}
    66  		for _, target := range vv {
    67  			if target == "-" {
    68  				parsed = append(parsed, 0)
    69  				continue
    70  			}
    71  
    72  			t, err := strconv.ParseUint(target, 10, 64)
    73  			if err != nil {
    74  				return err
    75  			}
    76  			parsed = append(parsed, t)
    77  		}
    78  		exists = append(exists, values[0])
    79  
    80  		n, present := iocs[values[0]]
    81  		if !present {
    82  			n = IOCountersStat{Name: values[0]}
    83  		}
    84  		if mode == "inb" {
    85  			n.BytesRecv = parsed[0]
    86  			n.BytesSent = parsed[1]
    87  		} else {
    88  			n.PacketsRecv = parsed[0]
    89  			n.Errin = parsed[1]
    90  			n.PacketsSent = parsed[2]
    91  			n.Errout = parsed[3]
    92  			n.Dropin = parsed[4]
    93  			n.Dropout = parsed[4]
    94  		}
    95  
    96  		iocs[n.Name] = n
    97  	}
    98  	return nil
    99  }
   100  
   101  func IOCounters(pernic bool) ([]IOCountersStat, error) {
   102  	return IOCountersWithContext(context.Background(), pernic)
   103  }
   104  
   105  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
   106  	netstat, err := exec.LookPath("netstat")
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	iocs := make(map[string]IOCountersStat)
   119  
   120  	lines := strings.Split(string(out), "\n")
   121  	ret := make([]IOCountersStat, 0, len(lines)-1)
   122  
   123  	err = ParseNetstat(string(out), "inb", iocs)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	err = ParseNetstat(string(out2), "ind", iocs)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	for _, ioc := range iocs {
   133  		ret = append(ret, ioc)
   134  	}
   135  
   136  	if pernic == false {
   137  		return getIOCountersAll(ret)
   138  	}
   139  
   140  	return ret, nil
   141  }
   142  
   143  // NetIOCountersByFile is an method which is added just a compatibility for linux.
   144  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
   145  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
   146  }
   147  
   148  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
   149  	return IOCounters(pernic)
   150  }
   151  
   152  func FilterCounters() ([]FilterStat, error) {
   153  	return FilterCountersWithContext(context.Background())
   154  }
   155  
   156  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   157  	return nil, errors.New("NetFilterCounters not implemented for openbsd")
   158  }
   159  
   160  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   161  	return ConntrackStatsWithContext(context.Background(), percpu)
   162  }
   163  
   164  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   165  	return nil, common.ErrNotImplementedError
   166  }
   167  
   168  // NetProtoCounters returns network statistics for the entire system
   169  // If protocols is empty then all protocols are returned, otherwise
   170  // just the protocols in the list are returned.
   171  // Not Implemented for OpenBSD
   172  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   173  	return ProtoCountersWithContext(context.Background(), protocols)
   174  }
   175  
   176  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   177  	return nil, errors.New("NetProtoCounters not implemented for openbsd")
   178  }
   179  
   180  func parseNetstatLine(line string) (ConnectionStat, error) {
   181  	f := strings.Fields(line)
   182  	if len(f) < 5 {
   183  		return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
   184  	}
   185  
   186  	var netType, netFamily uint32
   187  	switch f[0] {
   188  	case "tcp":
   189  		netType = syscall.SOCK_STREAM
   190  		netFamily = syscall.AF_INET
   191  	case "udp":
   192  		netType = syscall.SOCK_DGRAM
   193  		netFamily = syscall.AF_INET
   194  	case "tcp6":
   195  		netType = syscall.SOCK_STREAM
   196  		netFamily = syscall.AF_INET6
   197  	case "udp6":
   198  		netType = syscall.SOCK_DGRAM
   199  		netFamily = syscall.AF_INET6
   200  	default:
   201  		return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
   202  	}
   203  
   204  	laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
   205  	if err != nil {
   206  		return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
   207  	}
   208  
   209  	n := ConnectionStat{
   210  		Fd:     uint32(0), // not supported
   211  		Family: uint32(netFamily),
   212  		Type:   uint32(netType),
   213  		Laddr:  laddr,
   214  		Raddr:  raddr,
   215  		Pid:    int32(0), // not supported
   216  	}
   217  	if len(f) == 6 {
   218  		n.Status = f[5]
   219  	}
   220  
   221  	return n, nil
   222  }
   223  
   224  func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
   225  	parse := func(l string) (Addr, error) {
   226  		matches := portMatch.FindStringSubmatch(l)
   227  		if matches == nil {
   228  			return Addr{}, fmt.Errorf("wrong addr, %s", l)
   229  		}
   230  		host := matches[1]
   231  		port := matches[2]
   232  		if host == "*" {
   233  			switch family {
   234  			case syscall.AF_INET:
   235  				host = "0.0.0.0"
   236  			case syscall.AF_INET6:
   237  				host = "::"
   238  			default:
   239  				return Addr{}, fmt.Errorf("unknown family, %d", family)
   240  			}
   241  		}
   242  		lport, err := strconv.Atoi(port)
   243  		if err != nil {
   244  			return Addr{}, err
   245  		}
   246  		return Addr{IP: host, Port: uint32(lport)}, nil
   247  	}
   248  
   249  	laddr, err = parse(local)
   250  	if remote != "*.*" { // remote addr exists
   251  		raddr, err = parse(remote)
   252  		if err != nil {
   253  			return laddr, raddr, err
   254  		}
   255  	}
   256  
   257  	return laddr, raddr, err
   258  }
   259  
   260  // Return a list of network connections opened.
   261  func Connections(kind string) ([]ConnectionStat, error) {
   262  	return ConnectionsWithContext(context.Background(), kind)
   263  }
   264  
   265  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   266  	var ret []ConnectionStat
   267  
   268  	args := []string{"-na"}
   269  	switch strings.ToLower(kind) {
   270  	default:
   271  		fallthrough
   272  	case "":
   273  		fallthrough
   274  	case "all":
   275  		fallthrough
   276  	case "inet":
   277  		// nothing to add
   278  	case "inet4":
   279  		args = append(args, "-finet")
   280  	case "inet6":
   281  		args = append(args, "-finet6")
   282  	case "tcp":
   283  		args = append(args, "-ptcp")
   284  	case "tcp4":
   285  		args = append(args, "-ptcp", "-finet")
   286  	case "tcp6":
   287  		args = append(args, "-ptcp", "-finet6")
   288  	case "udp":
   289  		args = append(args, "-pudp")
   290  	case "udp4":
   291  		args = append(args, "-pudp", "-finet")
   292  	case "udp6":
   293  		args = append(args, "-pudp", "-finet6")
   294  	case "unix":
   295  		return ret, common.ErrNotImplementedError
   296  	}
   297  
   298  	netstat, err := exec.LookPath("netstat")
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	out, err := invoke.CommandWithContext(ctx, netstat, args...)
   303  
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  	lines := strings.Split(string(out), "\n")
   308  	for _, line := range lines {
   309  		if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
   310  			continue
   311  		}
   312  		n, err := parseNetstatLine(line)
   313  		if err != nil {
   314  			continue
   315  		}
   316  
   317  		ret = append(ret, n)
   318  	}
   319  
   320  	return ret, nil
   321  }