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 }