1module MCollective 2 module Util 3 module PuppetServerAddressValidation 4 5 class Host 6 require 'ipaddr' 7 attr_reader :name, :port 8 9 INT_REGEX = /\A\d+\Z/ 10 PORT_REGEX = /\A\d+\Z/ 11 IPV6_FORMAT_REGEX = /\A\[[a-fA-F0-9\:\.]*\](\:\d*)?/ 12 NAME_REGEX = /\A(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\Z/ 13 14 def initialize(server) 15 @server = server 16 @name, @port = parse_name_and_port 17 end 18 19 private 20 21 def parse_name_and_port 22 name, port = nil 23 if ipv6_server? 24 # IPv6 address, without a port 25 name = @server 26 elsif @server =~ IPV6_FORMAT_REGEX 27 # IPv6 url "[<ipv6_address>]:<port>", as in rfc2732 28 name, port = @server[1..@server.size].split(']') 29 if port 30 # Remove the initial colon 31 port = port[1..port.size] 32 end 33 else 34 # Parse the port only if we have a single colon, 35 # to avoid splitting invalid IPv6 addresses 36 if @server && @server.count(':') == 1 37 name, port = @server.split(':') 38 else 39 name = @server 40 end 41 end 42 return name, port 43 end 44 45 def process_ip (addr) 46 # NB: an int is a valid IPv6 addr for the ipaddr lib on ruby 1.8.7! 47 unless addr =~ INT_REGEX 48 begin 49 IPAddr.new(addr) 50 rescue 51 end 52 end 53 end 54 55 public 56 57 def valid_text_name? 58 @name =~ NAME_REGEX 59 end 60 61 def ipv6_server? 62 ip = process_ip(@server) 63 ip && ip.ipv6? 64 end 65 66 def valid_ip_name? 67 ip = process_ip(@name) 68 ip && (ip.ipv4? || ip.ipv6?) 69 end 70 71 def valid_port? 72 @port =~ PORT_REGEX 73 end 74 end 75 76 77 def self.validate_server(server) 78 79 host = Host.new(server) 80 81 if host.name && !(host.valid_ip_name? || host.valid_text_name?) 82 raise "The hostname '%s' is not a valid hostname" % host.name 83 end 84 85 if host.port && !host.valid_port? 86 raise "The port '%s' is not a valid puppet master port" % host.port 87 end 88 end 89 90 def self.parse_name_and_port_of(server) 91 host = Host.new(server) 92 return host.name, host.port 93 end 94 95 end 96 end 97end 98