#!/usr/bin/env ruby require 'shellwords' require 'ipaddress' require 'resolv' class R def initialize opts=nil @resolv = Resolv::DNS.new opts end IN = Resolv::DNS::Resource::IN def a( q) @resolv.getresources( q, IN::A) end def aaaa( q) @resolv.getresources( q, IN::AAAA) end def txt( q) @resolv.getresources( q, IN::TXT) end def mx( q) @resolv.getresources( q, IN::MX) end def ip q a(q) + aaaa(q) end end $resolv = R.new def bb d $resolv.txt( d).map do |l| v, *as = l.data.split( ' ') if "v=spf1" == v as.map do |a| case a when /\Aa:(.*)\z/ $resolv.ip( $1).map( &:address) when "mx" $resolv.mx( d).map do |l| $resolv.ip( l.exchange.to_s).map( &:address) end when /\Aip[46]:(.*)\z/ then $1 when /\A(?:redirect=|include:)(.*)\z/ then bb $1 when /\A[-~+]all/ then nil else nil end end end end end ARGV. flat_map do |d| bb( d). flatten. compact. uniq. map {|x| IPAddress.parse( x).first.to_string rescue ArgumentError }. select {|x| String === x }. uniq. map {|x| [x, d]} end. group_by {|(x,d)| x }. each {|x,xs| xs.map! &:last }. sort_by {|x,ds| x = IPAddress.parse x; [x.class.name, x] }. each do |(x,ds)| puts "#{x} permit # #{ds.uniq.sort.join ' '}" end