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 }