class NetworkUtils::Port

Simple class to work with ports Allows to get random port number, check availability, etc.

Constants

IANA_PORT_RANGE

Internet Assigned Numbers Authority suggested range

PORT_LOOKUP_RETRY_LIMIT

The max limit for port lookup retries

SERVICES_FILE_PATH

Current system's IANA port assignments file Cound be changed using SERVICES_FILE_PATH ENV variable

Public Class Methods

available?(port, host = '127.0.0.1', timeout = 1) click to toggle source

Checks if the port is available (free) on the host

@example

NetworkUtils::Port.available?(9292)
NetworkUtils::Port.available?(80, 'google.com', 100)
NetworkUtils::Port.free?(80, 'google.com', 100)
NetworkUtils::Port.free?(80, 'google.com', 100)

@param [Integer] port the port we want to check availability of @param [String] host the host we want to check on (default: 127.0.0.1) @param [Timeout] timeout the time (seconds) we ready to wait (default: 1)

@return [Boolean] result of the check (true — port is free to use, false — the port is occupied)

# File lib/network_utils/port.rb, line 39
def self.available?(port, host = '127.0.0.1', timeout = 1)
  return false unless port && host && timeout && timeout.positive?

  Timeout.timeout(timeout) do
    TCPSocket.new(host, port).close
    false
  end
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
  true
rescue SocketError, Timeout::Error, Errno::EADDRNOTAVAIL
  false
end
Also aliased as: free?
free?(port, host = '127.0.0.1', timeout = 1)

available? → free?

Alias for: available?
name(port) click to toggle source

Checks the IANA port assignments file and returns possible service name

@example

NetworkUtils::Port.name(8080) => ["http-alt"]

@note Just a convinience method over ::service

@param [Integer] port the port we want to check out

@return [Array] port's possible serivce names

# File lib/network_utils/port.rb, line 147
def self.name(port)
  self.service(port).map { |s| s.fetch(:name) }.uniq
end
occupied?(port, host = '127.0.0.1', timeout = 1)

opened? → occupied?

Alias for: opened?
opened?(port, host = '127.0.0.1', timeout = 1) click to toggle source

Checks if the port is opened (occupied / being listened) on the host

@example

NetworkUtils::Port.opened?(443, 'google.com')
NetworkUtils::Port.opened?(80, 'google.com', 1)
NetworkUtils::Port.occupied?(80, 'google.com', 1)
NetworkUtils::Port.occupied?(80, 'google.com', 1)

@note Just the opposite of `available?`

@param [Integer] port the port we want to check availability of @param [String] host the host we want to check on (default: 127.0.0.1) @param [Timeout] timeout the time (seconds) we ready to wait (default: 1)

@return [Boolean] result of the check (true — the port is being listened, false — the port is free)

# File lib/network_utils/port.rb, line 67
def self.opened?(port, host = '127.0.0.1', timeout = 1)
  !available?(port, host, timeout)
end
Also aliased as: occupied?
random() click to toggle source

Generates random port from IANA recommended range

@note

The Internet Assigned Numbers Authority (IANA) suggests the
range 49152 to 65535 (215+214 to 216−1) for dynamic or private ports.

@return [Boolean] port the port from the IANA suggested range

# File lib/network_utils/port.rb, line 78
def self.random
  rand(IANA_PORT_RANGE)
end
random_free() click to toggle source

Generates random port from IANA recommended range which is free on the localhost

@note

The Internet Assigned Numbers Authority (IANA) suggests the
range 49152 to 65535 (215+214 to 216−1) for dynamic or private ports.

@return [Boolean] port the port from the IANA suggested range which is also free on the current machine

# File lib/network_utils/port.rb, line 89
def self.random_free
  PORT_LOOKUP_RETRY_LIMIT.times do
    port = random
    return port if available?(port)
  end

  nil
end
service(port) click to toggle source

Checks the IANA port assignments file for port info

@example

NetworkUtils::Port.service(8080)
   => [
        {:name=>"http-alt", :port=>8080, :protocol=>:udp, :description=>"HTTP Alternate (see port 80)"},
        {:name=>"http-alt", :port=>8080, :protocol=>:tcp, :description=>"HTTP Alternate (see port 80)"}
      ]

@note When looking just for a short name, use ::name

@param [Integer] port the port we want to check out

@return [Array] port services info with the :name, :port, :protocol, :description

# File lib/network_utils/port.rb, line 112
def self.service(port)
  # check the IANA port assignments file (default or custom)
  services_file = ENV['SERVICES_FILE_PATH'] || SERVICES_FILE_PATH
  return nil unless File.exist?(services_file)

  # read the file and extract info (ony lines matching "bacnet   47808/tcp   # Building Automation and Control Networks" format)
  services = File.read(services_file).lines.map do |line|
    line_elements = line.split(/\s+/)

    next unless line_elements[1] =~ /\d+\//

    known_port, known_protocol = line_elements[1].split('/')

    {
      name: line_elements[0],
      port: known_port.to_i,
      protocol: known_protocol.to_sym,
      description: line_elements[3..-1]&.join(' ')
    }
  end

  # extract infor about the requested port
  Array.wrap(services.compact.find_all { |s| s[:port] == port })
end