github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/net/net_posix.go (about)

     1  //go:build darwin
     2  
     3  package net
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"github.com/isyscore/isc-gobase/system/common"
     9  	"net"
    10  	"strconv"
    11  	"strings"
    12  	"syscall"
    13  )
    14  
    15  // Connections Return a list of network connections opened.
    16  func Connections(kind string) ([]ConnectionStat, error) {
    17  	return ConnectionsWithContext(context.Background(), kind)
    18  }
    19  
    20  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
    21  	return ConnectionsPid(kind, 0)
    22  }
    23  
    24  // ConnectionsMax Return a list of network connections opened returning at most `max`
    25  // connections for each running process.
    26  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
    27  	return ConnectionsMaxWithContext(context.Background(), kind, max)
    28  }
    29  
    30  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
    31  	return []ConnectionStat{}, common.ErrNotImplementedError
    32  }
    33  
    34  // ConnectionsPid Return a list of network connections opened by a process.
    35  func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
    36  	return ConnectionsPidWithContext(context.Background(), kind, pid)
    37  }
    38  
    39  func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
    40  	var ret []ConnectionStat
    41  
    42  	args := []string{"-i"}
    43  	switch strings.ToLower(kind) {
    44  	default:
    45  		fallthrough
    46  	case "":
    47  		fallthrough
    48  	case "all":
    49  		fallthrough
    50  	case "inet":
    51  		args = append(args, "tcp", "-i", "udp")
    52  	case "inet4":
    53  		args = append(args, "4")
    54  	case "inet6":
    55  		args = append(args, "6")
    56  	case "tcp":
    57  		args = append(args, "tcp")
    58  	case "tcp4":
    59  		args = append(args, "4tcp")
    60  	case "tcp6":
    61  		args = append(args, "6tcp")
    62  	case "udp":
    63  		args = append(args, "udp")
    64  	case "udp4":
    65  		args = append(args, "4udp")
    66  	case "udp6":
    67  		args = append(args, "6udp")
    68  	case "unix":
    69  		args = []string{"-U"}
    70  	}
    71  
    72  	r, err := common.CallLsofWithContext(ctx, invoke, pid, args...)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	for _, rr := range r {
    77  		if strings.HasPrefix(rr, "COMMAND") {
    78  			continue
    79  		}
    80  		n, err := parseNetLine(rr)
    81  		if err != nil {
    82  
    83  			continue
    84  		}
    85  
    86  		ret = append(ret, n)
    87  	}
    88  
    89  	return ret, nil
    90  }
    91  
    92  var constMap = map[string]int{
    93  	"unix": syscall.AF_UNIX,
    94  	"TCP":  syscall.SOCK_STREAM,
    95  	"UDP":  syscall.SOCK_DGRAM,
    96  	"IPv4": syscall.AF_INET,
    97  	"IPv6": syscall.AF_INET6,
    98  }
    99  
   100  func parseNetLine(line string) (ConnectionStat, error) {
   101  	f := strings.Fields(line)
   102  	if len(f) < 8 {
   103  		return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
   104  	}
   105  
   106  	if len(f) == 8 {
   107  		f = append(f, f[7])
   108  		f[7] = "unix"
   109  	}
   110  
   111  	pid, err := strconv.Atoi(f[1])
   112  	if err != nil {
   113  		return ConnectionStat{}, err
   114  	}
   115  	fd, err := strconv.Atoi(strings.Trim(f[3], "u"))
   116  	if err != nil {
   117  		return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3])
   118  	}
   119  	netFamily, ok := constMap[f[4]]
   120  	if !ok {
   121  		return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4])
   122  	}
   123  	netType, ok := constMap[f[7]]
   124  	if !ok {
   125  		return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7])
   126  	}
   127  
   128  	var laddr, raddr Addr
   129  	if f[7] == "unix" {
   130  		laddr.IP = f[8]
   131  	} else {
   132  		laddr, raddr, err = parseNetAddr(f[8])
   133  		if err != nil {
   134  			return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8])
   135  		}
   136  	}
   137  
   138  	n := ConnectionStat{
   139  		Fd:     uint32(fd),
   140  		Family: uint32(netFamily),
   141  		Type:   uint32(netType),
   142  		Laddr:  laddr,
   143  		Raddr:  raddr,
   144  		Pid:    int32(pid),
   145  	}
   146  	if len(f) == 10 {
   147  		n.Status = strings.Trim(f[9], "()")
   148  	}
   149  
   150  	return n, nil
   151  }
   152  
   153  func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) {
   154  	parse := func(l string) (Addr, error) {
   155  		host, port, err := net.SplitHostPort(l)
   156  		if err != nil {
   157  			return Addr{}, fmt.Errorf("wrong addr, %s", l)
   158  		}
   159  		lport, err := strconv.Atoi(port)
   160  		if err != nil {
   161  			return Addr{}, err
   162  		}
   163  		return Addr{IP: host, Port: uint32(lport)}, nil
   164  	}
   165  
   166  	addrs := strings.Split(line, "->")
   167  	if len(addrs) == 0 {
   168  		return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line)
   169  	}
   170  	laddr, err = parse(addrs[0])
   171  	if len(addrs) == 2 { // remote addr exists
   172  		raddr, err = parse(addrs[1])
   173  		if err != nil {
   174  			return laddr, raddr, err
   175  		}
   176  	}
   177  
   178  	return laddr, raddr, err
   179  }
   180  
   181  // ConnectionsPidMax Return up to `max` network connections opened by a process.
   182  func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
   183  	return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
   184  }
   185  
   186  func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   187  	return []ConnectionStat{}, common.ErrNotImplementedError
   188  }
   189  
   190  // ConnectionsWithoutUids Return a list of network connections opened, omitting `Uids`.
   191  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   192  // removed from the API in the future.
   193  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   194  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   195  }
   196  
   197  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   198  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   199  }
   200  
   201  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   202  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   203  }
   204  
   205  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   206  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   207  }
   208  
   209  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   210  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   211  }
   212  
   213  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   214  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   215  }
   216  
   217  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   218  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
   219  }
   220  
   221  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   222  	return []ConnectionStat{}, common.ErrNotImplementedError
   223  }