lib/mongo/address/validator.rb
# frozen_string_literal: true
# rubocop:todo all
# Copyright (C) 2017-2020 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Mongo
class Address
# @api private
module Validator
# Takes an address string in ipv4/ipv6/hostname/socket path format and
# validates its format.
def validate_address_str!(address_str)
case address_str
when /\A\[[\d:]+\](?::(\d+))?\z/
# ipv6 with optional port
if port_str = $1
validate_port_str!(port_str)
end
when /\A\//, /\.sock\z/
# Unix socket path.
# Spec requires us to validate that the path has no unescaped
# slashes, but if this were to be the case, parsing would have
# already failed elsewhere because the URI would've been split in
# a weird place.
# The spec also allows relative socket paths and requires that
# socket paths end in ".sock". We accept all paths but special case
# the .sock extension to avoid relative paths falling into the
# host:port case below.
when /[\/\[\]]/
# Not a host:port nor an ipv4 address with optional port.
# Possibly botched ipv6 address with e.g. port delimiter present and
# port missing, or extra junk before or after.
raise Error::InvalidAddress,
"Invalid hostname: #{address_str}"
when /:.*:/m
raise Error::InvalidAddress,
"Multiple port delimiters are not allowed: #{address_str}"
else
# host:port or ipv4 address with optional port number
host, port = address_str.split(':')
if host.empty?
raise Error::InvalidAddress, "Host is empty: #{address_str}"
end
validate_hostname!(host)
if port && port.empty?
raise Error::InvalidAddress, "Port is empty: #{address_str}"
end
validate_port_str!(port)
end
end
private
# Validates format of the hostname, in particular for further use as
# the origin in same origin verification.
#
# The hostname must have been normalized to remove the trailing dot if
# it was obtained from a DNS record. This method prohibits trailing dots.
def validate_hostname!(host)
# Since we are performing same origin verification during SRV
# processing, prohibit leading dots in hostnames, trailing dots
# and runs of multiple dots. DNS resolution of SRV records yields
# hostnames with trailing dots, those trailing dots are removed
# during normalization process prior to validation.
if host.start_with?('.')
raise Error::InvalidAddress, "Hostname cannot start with a dot: #{host}"
end
if host.end_with?('.')
raise Error::InvalidAddress, "Hostname cannot end with a dot: #{host}"
end
if host.include?('..')
raise Error::InvalidAddress, "Runs of multiple dots are not allowed in hostname: #{host}"
end
end
def validate_port_str!(port)
unless port.nil? || (port.length > 0 && port.to_i > 0 && port.to_i <= 65535)
raise Error::InvalidAddress,
"Invalid port: #{port}. Port must be an integer greater than 0 and less than 65536"
end
end
end
end
end