Skip to content

Commit

Permalink
Merge pull request #721 from kpengboy/resolve-ipv6
Browse files Browse the repository at this point in the history
(MODULES-5645) Choose correct IP version for hostname resolution
  • Loading branch information
eputnam committed Sep 27, 2017
2 parents 7024ffa + cb1bc3d commit 8f446aa
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 28 deletions.
22 changes: 20 additions & 2 deletions lib/puppet/type/firewall.rb
Expand Up @@ -134,8 +134,17 @@
EOS

munge do |value|
case @resource[:provider]
when :iptables
protocol = :IPv4
when :ip6tables
protocol = :IPv6
else
self.fail("cannot work out protocol family")
end

begin
@resource.host_to_mask(value)
@resource.host_to_mask(value, protocol)
rescue Exception => e
self.fail("host_to_ip failed for #{value}, exception #{e}")
end
Expand Down Expand Up @@ -184,8 +193,17 @@
EOS

munge do |value|
case @resource[:provider]
when :iptables
protocol = :IPv4
when :ip6tables
protocol = :IPv6
else
self.fail("cannot work out protocol family")
end

begin
@resource.host_to_mask(value)
@resource.host_to_mask(value, protocol)
rescue Exception => e
self.fail("host_to_ip failed for #{value}, exception #{e}")
end
Expand Down
38 changes: 31 additions & 7 deletions lib/puppet/util/firewall.rb
Expand Up @@ -88,7 +88,9 @@ def string_to_port(value, proto)
end
end

# Takes an address and returns it in CIDR notation.
# Takes an address and protocol and returns the address in CIDR notation.
#
# The protocol is only used when the address is a hostname.
#
# If the address is:
#
Expand All @@ -105,27 +107,49 @@ def string_to_port(value, proto)
# - Any address with a resulting prefix length of zero:
# It will return nil which is equivilent to not specifying an address
#
def host_to_ip(value)
def host_to_ip(value, proto = nil)
begin
value = Puppet::Util::IPCidr.new(value)
rescue
value = Puppet::Util::IPCidr.new(Resolv.getaddress(value))
family = case proto
when :IPv4
Socket::AF_INET
when :IPv6
Socket::AF_INET6
when nil
raise ArgumentError, "Proto must be specified for a hostname"
else
raise ArgumentError, "Unsupported address family: #{proto}"
end

new_value = nil
Resolv.each_address(value) do |addr|
begin
new_value = Puppet::Util::IPCidr.new(addr, family)
break
rescue
end
end

raise "Failed to resolve hostname #{value}" unless new_value != nil
value = new_value
end

return nil if value.prefixlen == 0
value.cidr
end

# Takes an address mask and converts the host portion to CIDR notation.
# Takes an address mask and protocol and converts the host portion to CIDR
# notation.
#
# This takes into account you can negate a mask but follows all rules
# defined in host_to_ip for the host/address part.
#
def host_to_mask(value)
def host_to_mask(value, proto)
match = value.match /(!)\s?(.*)$/
return host_to_ip(value) unless match
return host_to_ip(value, proto) unless match

cidr = host_to_ip(match[2])
cidr = host_to_ip(match[2], proto)
return nil if cidr == nil
"#{match[1]} #{cidr}"
end
Expand Down
4 changes: 2 additions & 2 deletions lib/puppet/util/ipcidr.rb
Expand Up @@ -4,9 +4,9 @@
module Puppet
module Util
class IPCidr < IPAddr
def initialize(ipaddr)
def initialize(ipaddr, family = Socket::AF_UNSPEC)
begin
super(ipaddr)
super(ipaddr, family)
rescue ArgumentError => e
if e.message =~ /invalid address/
raise ArgumentError, "Invalid address from IPAddr.new: #{ipaddr}"
Expand Down
37 changes: 20 additions & 17 deletions spec/unit/puppet/util/firewall_spec.rb
Expand Up @@ -14,8 +14,9 @@
describe '#host_to_ip' do
subject { resource }
it {
expect(Resolv).to receive(:getaddress).with('puppetlabs.com').and_return('96.126.112.51')
expect(subject.host_to_ip('puppetlabs.com')).to eql '96.126.112.51/32'
expect(Resolv).to receive(:each_address).at_least(:once).with('puppetlabs.com').and_yield('96.126.112.51').and_yield('2001:DB8:4650::13:8A')
expect(subject.host_to_ip('puppetlabs.com', :IPv4)).to eql '96.126.112.51/32'
expect(subject.host_to_ip('puppetlabs.com', :IPv6)).to eql '2001:db8:4650::13:8a/128'
}
it { expect(subject.host_to_ip('96.126.112.51')).to eql '96.126.112.51/32' }
it { expect(subject.host_to_ip('96.126.112.51/32')).to eql '96.126.112.51/32' }
Expand All @@ -28,22 +29,24 @@
describe '#host_to_mask' do
subject { resource }
it {
expect(Resolv).to receive(:getaddress).at_least(:once).with('puppetlabs.com').and_return('96.126.112.51')
expect(subject.host_to_mask('puppetlabs.com')).to eql '96.126.112.51/32'
expect(subject.host_to_mask('!puppetlabs.com')).to eql '! 96.126.112.51/32'
expect(Resolv).to receive(:each_address).at_least(:once).with('puppetlabs.com').and_yield('96.126.112.51').and_yield('2001:DB8:4650::13:8A')
expect(subject.host_to_mask('puppetlabs.com', :IPv4)).to eql '96.126.112.51/32'
expect(subject.host_to_mask('!puppetlabs.com', :IPv4)).to eql '! 96.126.112.51/32'
expect(subject.host_to_mask('puppetlabs.com', :IPv6)).to eql '2001:db8:4650::13:8a/128'
expect(subject.host_to_mask('!puppetlabs.com', :IPv6)).to eql '! 2001:db8:4650::13:8a/128'
}
it { expect(subject.host_to_mask('96.126.112.51')).to eql '96.126.112.51/32' }
it { expect(subject.host_to_mask('!96.126.112.51')).to eql '! 96.126.112.51/32' }
it { expect(subject.host_to_mask('96.126.112.51/32')).to eql '96.126.112.51/32' }
it { expect(subject.host_to_mask('! 96.126.112.51/32')).to eql '! 96.126.112.51/32' }
it { expect(subject.host_to_mask('2001:db8:85a3:0:0:8a2e:370:7334')).to eql '2001:db8:85a3::8a2e:370:7334/128' }
it { expect(subject.host_to_mask('!2001:db8:85a3:0:0:8a2e:370:7334')).to eql '! 2001:db8:85a3::8a2e:370:7334/128' }
it { expect(subject.host_to_mask('2001:db8:1234::/48')).to eql '2001:db8:1234::/48' }
it { expect(subject.host_to_mask('! 2001:db8:1234::/48')).to eql '! 2001:db8:1234::/48' }
it { expect(subject.host_to_mask('0.0.0.0/0')).to eql nil }
it { expect(subject.host_to_mask('!0.0.0.0/0')).to eql nil }
it { expect(subject.host_to_mask('::/0')).to eql nil }
it { expect(subject.host_to_mask('! ::/0')).to eql nil }
it { expect(subject.host_to_mask('96.126.112.51', :IPv4)).to eql '96.126.112.51/32' }
it { expect(subject.host_to_mask('!96.126.112.51', :IPv4)).to eql '! 96.126.112.51/32' }
it { expect(subject.host_to_mask('96.126.112.51/32', :IPv4)).to eql '96.126.112.51/32' }
it { expect(subject.host_to_mask('! 96.126.112.51/32', :IPv4)).to eql '! 96.126.112.51/32' }
it { expect(subject.host_to_mask('2001:db8:85a3:0:0:8a2e:370:7334', :IPv6)).to eql '2001:db8:85a3::8a2e:370:7334/128' }
it { expect(subject.host_to_mask('!2001:db8:85a3:0:0:8a2e:370:7334', :IPv6)).to eql '! 2001:db8:85a3::8a2e:370:7334/128' }
it { expect(subject.host_to_mask('2001:db8:1234::/48', :IPv6)).to eql '2001:db8:1234::/48' }
it { expect(subject.host_to_mask('! 2001:db8:1234::/48', :IPv6)).to eql '! 2001:db8:1234::/48' }
it { expect(subject.host_to_mask('0.0.0.0/0', :IPv4)).to eql nil }
it { expect(subject.host_to_mask('!0.0.0.0/0', :IPv4)).to eql nil }
it { expect(subject.host_to_mask('::/0', :IPv6)).to eql nil }
it { expect(subject.host_to_mask('! ::/0', :IPv6)).to eql nil }
end

describe '#icmp_name_to_number' do
Expand Down

0 comments on commit 8f446aa

Please sign in to comment.