class Inspec::Resources::AixPorts
def info
def info ports_via_netstat || ports_via_lsof end
def parse_net_address(net_addr, protocol)
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
def parse_netstat_line(line)
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
def ports_via_lsof
def ports_via_lsof return nil unless inspec.command('lsof').exist? LsofPorts.new(inspec).info end
def ports_via_netstat
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.zero? 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