Method: Inspec::Resources::LinuxPorts#parse_ss_line

Defined in:
lib/inspec/resources/port.rb

#parse_ss_line(line) ⇒ Object



541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'lib/inspec/resources/port.rb', line 541

def parse_ss_line(line)
  # parsed = line.split(/\s+/, 7)
  parsed = tokenize_ss_line(line)

  # ss only returns "tcp" and "udp" as the protocol. However, netstat would return
  # "tcp6" and "udp6" as necessary. In order to maintain backward compatibility, we
  # will manually modify the protocol value if the line we're parsing is an IPv6
  # entry.
  process_info = parsed[:process_info]
  protocol = parsed[:netid]
  protocol += "6" if process_info.include?("v6only:1")
  return nil unless ALLOWED_PROTOCOLS.include?(protocol)

  # parse the Local Address:Port
  # examples:
  #   *:22
  #   :::22
  #   10.0.2.15:1234
  #   ::ffff:10.0.2.15:9300
  #   fe80::a00:27ff:fe32:ed09%enp0s3:9200
  parsed_net_address = parsed[:local_addr].match(/(\S+):(\*|\d+)$/)
  return nil if parsed_net_address.nil?

  host = parsed_net_address[1]
  port = parsed_net_address[2]
  return nil if host.nil? && port.nil?

  # For backward compatibility with the netstat output, ensure the
  # port is stored as an integer
  port = port.to_i

  # for those "v4-but-listed-in-v6" entries, strip off the
  # leading IPv6 value at the beginning
  # example: ::ffff:10.0.2.15:9200
  host.delete!("::ffff:") if host.start_with?("::ffff:")

  # To remove brackets that might surround the IPv6 address
  # example: [::] and [fe80::dc11:b9b6:514b:134]%eth0:123
  host = host.tr("[]", "")

  # if there's an interface name in the local address, which is common for
  # IPv6 listeners, strip that out too.
  # example: fe80::a00:27ff:fe32:ed09%enp0s3
  host = host.split("%").first

  # if host is "*", replace with "0.0.0.0" to maintain backward compatibility with
  # the netstat-provided data
  host = "0.0.0.0" if host == "*"

  # in case process list parsing is not successfull
  process = nil
  pid = nil

  # parse process and pid from the process list
  #
  # remove the "users:((" and  "))" parts
  # input: users:((\"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8))
  # res: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
  process_list_match = parsed[:process_info].match(/users:\(\((.+)\)\)/)
  if process_list_match
    # list entires are seperated by "," the braces can also be removed
    # input: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
    # res: ["\"nginx\",pid=583,fd=8", "\"nginx\",pid=582,fd=8", "\"nginx\",pid=580,fd=8", "\"nginx\",pid=579,fd=8"]
    process_list = process_list_match[1].split("),(")
    # To stay backwards compatible with netstat we need to select
    # the last element in the resulting array.
    # res: "\"nginx\",pid=579,fd=8"

    # parse the process name from the process list
    process_match = process_list.last.match(/^\"(\S+)\"/)
    process = process_match.nil? ? nil : process_match[1]

    # parse the PID from the process list
    pid_match = process_list.last.match(/pid=(\d+)/)
    pid = pid_match.nil? ? nil : pid_match[1].to_i
  end

  {
    "port" => port,
    "address" => host,
    "protocol" => protocol,
    "process" => process,
    "pid" => pid,
  }
end