Class: NetworkUtils::Port

Inherits:
Object
  • Object
show all
Defined in:
lib/network_utils/port.rb

Overview

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

Constant Summary collapse

PORT_LOOKUP_RETRY_LIMIT =

The max limit for port lookup retries

50
IANA_PORT_RANGE =

Internet Assigned Numbers Authority suggested range

(49_152..65_535).freeze
SERVICES_FILE_PATH =

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

'/etc/services'

Class Method Summary collapse

Class Method Details

.available?(port, host = '127.0.0.1', timeout = 1) ⇒ Boolean Also known as: free?

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

Examples:

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)

Parameters:

  • port (Integer)

    the port we want to check availability of

  • host (String) (defaults to: '127.0.0.1')

    the host we want to check on (default: 127.0.0.1)

  • timeout (Timeout) (defaults to: 1)

    the time (seconds) we ready to wait (default: 1)

Returns:

  • (Boolean)

    result of the check (true — port is free to use, false — the port is occupied)



39
40
41
42
43
44
45
46
47
48
49
50
# 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

.name(port) ⇒ Array

Note:

Just a convinience method over ::service

Checks the IANA port assignments file and returns possible service name

Examples:

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

Parameters:

  • port (Integer)

    the port we want to check out

Returns:

  • (Array)

    port’s possible serivce names



147
148
149
# File 'lib/network_utils/port.rb', line 147

def self.name(port)
  self.service(port).map { |s| s.fetch(:name) }.uniq
end

.opened?(port, host = '127.0.0.1', timeout = 1) ⇒ Boolean Also known as: occupied?

Note:

Just the opposite of ‘available?`

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

Examples:

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)

Parameters:

  • port (Integer)

    the port we want to check availability of

  • host (String) (defaults to: '127.0.0.1')

    the host we want to check on (default: 127.0.0.1)

  • timeout (Timeout) (defaults to: 1)

    the time (seconds) we ready to wait (default: 1)

Returns:

  • (Boolean)

    result of the check (true — the port is being listened, false — the port is free)



67
68
69
# File 'lib/network_utils/port.rb', line 67

def self.opened?(port, host = '127.0.0.1', timeout = 1)
  !available?(port, host, timeout)
end

.randomBoolean

Note:

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

Generates random port from IANA recommended range

Returns:

  • (Boolean)

    port the port from the IANA suggested range



78
79
80
# File 'lib/network_utils/port.rb', line 78

def self.random
  rand(IANA_PORT_RANGE)
end

.random_freeBoolean

Note:

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

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

Returns:

  • (Boolean)

    port the port from the IANA suggested range which is also free on the current machine



89
90
91
92
93
94
95
96
# 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) ⇒ Array

Note:

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

Checks the IANA port assignments file for port info

Examples:

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)"}
      ]

Parameters:

  • port (Integer)

    the port we want to check out

Returns:

  • (Array)

    port services info with the :name, :port, :protocol, :description



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 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