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
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
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
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
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
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
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