class Inspec::Resources::AixPorts

Public Instance Methods

info() click to toggle source
# File lib/inspec/resources/port.rb, line 270
def info
  ports_via_netstat || ports_via_lsof
end
parse_net_address(net_addr, protocol) click to toggle source
# File lib/inspec/resources/port.rb, line 335
def parse_net_address(net_addr, protocol)
  # local/foreign addresses on AIX use a '.' to separate the addresss
  # from the port
  address, _sep, port = net_addr.rpartition(".")
  if protocol.eql?("tcp6") || protocol.eql?("udp6")
    ip6addr = address
    # AIX uses the wildcard character for ipv6 addresses listening on
    # all interfaces.
    ip6addr = "::" if ip6addr =~ /^\*$/

    # v6 addresses need to end in a double-colon when using
    # shorthand notation. netstat ends with a single colon.
    # IPAddr will fail to properly parse an address unless it
    # uses a double-colon for short-hand notation.
    ip6addr += ":" if ip6addr =~ /\w:$/

    begin
      ip_parser = IPAddr.new(ip6addr)
    rescue IPAddr::InvalidAddressError
      # This IP is not parsable. There appears to be a bug in netstat
      # output that truncates link-local IP addresses:
      # example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
      # actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
      #
      # in this example, the "5" is truncated making the netstat output
      # an invalid IP address.
      return [nil, nil]
    end

    # Check to see if this is a IPv4 address in a tcp6/udp6 line.
    # If so, don't put brackets around the IP or URI won't know how
    # to properly handle it.
    # example: f000000000000000 tcp6       0      0 127.0.0.1.8005          *.*                    LISTEN
    if ip_parser.ipv4?
      ip_addr = URI("addr://#{ip6addr}:#{port}")
      host = ip_addr.host
    else
      ip_addr = URI("addr://[#{ip6addr}]:#{port}")
      host = ip_addr.host[1..ip_addr.host.size - 2]
    end
  else
    ip4addr = address
    # In AIX the wildcard character is used to match all interfaces
    ip4addr = "0.0.0.0" if ip4addr =~ /^\*$/
    ip_addr = URI("addr://#{ip4addr}:#{port}")
    host = ip_addr.host
  end

  [host, port.to_i]
end
parse_netstat_line(line) click to toggle source
# File lib/inspec/resources/port.rb, line 300
def parse_netstat_line(line)
  # parse each line
  # 1 - Socket, 2 - Proto, 3 - Receive-Q, 4 - Send-Q, 5 - Local address, 6 - Foreign Address, 7 - State
  parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)/.match(line)
  return {} if parsed.nil?

  # parse ip4 and ip6 addresses
  protocol = parsed[2].downcase

  # detect protocol if not provided
  protocol += "6" if parsed[5].count(":") > 1 && %w{tcp udp}.include?(protocol)
  protocol.chop! if %w{tcp4 upd4}.include?(protocol)

  # extract host and port information
  host, port = parse_net_address(parsed[5], protocol)
  return {} if host.nil?

  # extract PID
  cmd = inspec.command("rmsock #{parsed[1]} tcpcb")
  parsed_pid = /^The socket (\S+) is being held by proccess (\d+) \((\S+)\)/.match(cmd.stdout)
  return {} if parsed_pid.nil?

  process = parsed_pid[3]
  pid = parsed_pid[2]
  pid = pid.to_i if pid =~ /^\d+$/

  {
    "port" => port,
    "address" => host,
    "protocol" => protocol,
    "process" => process,
    "pid" => pid,
  }
end
ports_via_lsof() click to toggle source
# File lib/inspec/resources/port.rb, line 274
def ports_via_lsof
  return nil unless inspec.command("lsof").exist?

  LsofPorts.new(inspec).info
end
ports_via_netstat() click to toggle source
# File lib/inspec/resources/port.rb, line 280
def ports_via_netstat
  return nil unless inspec.command("netstat").exist?

  cmd = inspec.command("netstat -Aan | grep LISTEN")
  return nil unless cmd.exit_status.to_i == 0

  ports = []
  # parse all lines
  cmd.stdout.each_line do |line|
    port_info = parse_netstat_line(line)

    # only push protocols we are interested in
    next unless %w{tcp tcp6 udp udp6}.include?(port_info["protocol"])

    ports.push(port_info)
  end

  ports
end