Skip to content
Browse files

Is now able to return SPF records

  • Loading branch information...
1 parent 24aeadc commit fc6aaaae4db69ca6ed245200b6bd5e6d571352c2 @jpf committed Jul 8, 2009
View
1 cache/.gitignore
@@ -0,0 +1 @@
+*.cache
View
13 lib/domain-profile/dns.rb
@@ -35,14 +35,13 @@ def initialize(input)
end
def spf
- rv = @query.map{|record|
- record if record[:type] == 'TXT'
+ @query.map{|record|
+ next unless record[:type] == 'TXT'
+ txt = DNSType.new(record).answer.gsub('"','')
+ next unless txt.match(/^v=spf/)
+ txt
}.compact
- if rv.empty?
- [DNSType.new(nil)]
- else
- rv.map { |record| DNSType.new(record) }
- end
+
end
def method_missing(type)
rv = @query.sort{|a,b| a[:answer] <=> b[:answer]}.map { |record|
View
41 lib/domain-profile/hostname.rb
@@ -0,0 +1,41 @@
+class Hostname
+ def shorten(input)
+ host = input.split('.')
+ host.shift if host.size > 2
+ host.join('.')
+ end
+ def simplify(input,match=nil)
+ lookup = {
+ '1and1.com' => '1and1',
+ 'akam.net' => 'akami',
+ 'amazon.com' => 'amazon',
+ 'domaincontrol.com' => 'godaddy',
+ 'easydns.com' => 'easydns',
+ 'easydns.net' => 'easydns',
+ 'easydns.org' => 'easydns',
+ 'emailsrvr.com' => 'enom',
+ 'gandi.net' => 'gandi',
+ 'godaddy.com' => 'godaddy',
+ 'google.com' => 'google',
+ 'googlemail.com' => 'google',
+ 'mailhop.org' => 'dyninc',
+ 'messagingengine.com' => 'enom',
+ 'mydyndns.org' => 'dyninc',
+ 'name-services.com' => 'enom',
+ 'nettica.com' => 'nettica',
+ 'pair.com' => 'pair',
+ 'rackspace.com' => 'rackspace',
+ 'registrar-servers.com' => 'enom',
+ 'secureserver.net' => 'godaddy',
+ 'softlayer.com' => 'softlayer',
+ 'ultradns.info' => 'ultradns',
+ 'ultradns.net' => 'ultradns',
+ 'ultradns.org' => 'ultradns',
+ }
+
+ return '' if (not defined? input) or input.nil?
+ return 'self' if (match and simplify(input) == simplify(match))
+ return lookup[input.downcase] if lookup[input.downcase]
+ input
+ end
+end
View
239 old.rb
@@ -0,0 +1,239 @@
+#!/usr/bin/env ruby
+
+require 'pp'
+# require 'dnsruby'
+# make README in markdown or a rdoc
+# examples folder
+# rdoc (rdoc.info)
+# rspec
+# license (MIT)
+# jeweler (gem that makes gems)
+
+
+#TODO: make this into a gem!
+def seconds_to_english(seconds)
+ time = [[:year,31556926], [:month,2629743], [:week,604800], [:day,86400], [:hour,3600], [:minute,60], [:second,1]]
+ seconds = seconds.to_i
+ out = []
+ time.each { |period,length|
+ count = seconds / length
+ if count > 0
+ seconds -= count * length
+
+ string = "#{count} #{period.to_s}"
+ string << 's' if count > 1
+ out.push(string)
+ end
+ }
+ out.join(', ')
+end
+
+
+class DomainInfo
+ def initialize(host)
+ @host = host
+ @whois = `sleep 0; whois #{host}`.split("\n")
+ @dns = {}
+ [:mx, :ns, :a, :txt].each { |type|
+ @dns[type] = `dig #{host} -t #{type.to_s}`
+ }
+ @ssl = `echo '' | openssl s_client -connect #{host}:443 2>&1`
+ end
+ attr_accessor :whois, :dns, :ssl
+end
+
+class Domain
+ def initialize(host)
+ @host = host
+ filename = "cache/cache-#{host}.cache"
+ if File.exists?(filename)
+ @info = open(filename) { |f| Marshal.load(f) }
+ else
+ @info = DomainInfo.new(host)
+ open(filename, "w") { |f| Marshal.dump(@info, f) }
+ end
+ end
+ def registrar
+ self.info.whois.grep(/Registrar:/).to_s.split(': ')[1]
+ end
+ def mx
+ method_missing(:mx).map { |rv|
+ rv[:host] = Hostname.new(rv[:answer].split(' ')[1])
+ rv
+ }
+ end
+ #TODO: this really belongs in the Hostname class
+ def ssl_issuer
+ begin
+ self.info.ssl.split("\n").grep(/^issuer=/).to_s.match(/O=([^\/]+)/)[1]
+ rescue
+ 'none'
+ end
+ end
+ def method_missing(type)
+ lookup = [:query,:ttl,:cl,:type,:answer]
+ h = Hash[]
+ self.info.dns[type].split("\n").grep(/^[^;]/).map { |line|
+ rv = Hash[*lookup.zip(line.gsub("\t\t","\t").split("\t")).flatten]
+ rv[:host] = Hostname.new(rv[:answer])
+ rv
+ }
+ end
+ attr_accessor :host, :info
+end
+
+#TODO: abstract this
+class Hostname
+ def initialize(host)
+ @host = host.to_s
+ end
+ def method_missing(type)
+ @host
+ end
+ def sld
+ @host.downcase.split('.')[-2..-1].join('.')
+ end
+ def ip
+ `dig #{@host} a +short`.chomp
+ end
+ def org
+ #TODO: Abstract this out to a whois class.
+ filename = "cache/#{@host}.cache"
+ if File.exists?(filename)
+ whois = open(filename) { |f| Marshal.load(f) }
+ else
+ whois = `sleep 0; whois #{@host}`.split("\n")
+ open(filename, "w") { |f| Marshal.dump(whois, f) }
+ end
+
+ begin
+ whois.grep(/^OrgName:/).to_s.match(/OrgName:\s+(.*)/)[1]
+ rescue
+ `./geoiplookup -f GeoIPOrg.dat #{@host}`.chomp.split(': ')[1]
+ end
+ end
+end
+
+class PaidFor
+ def initialize(domain)
+ @domain = domain
+ end
+ def registrar
+ self.to_symbol(@domain.registrar)
+ end
+ def ssl_issuer
+ self.to_symbol(@domain.ssl_issuer)
+ end
+ def hosting
+ @domain.a.map {|rv| self.to_symbol(rv[:host].org) }.uniq
+ end
+ def dns_hosting
+ @domain.ns.map { |rv| self.to_symbol(rv[:host].sld) }.uniq
+ end
+ def email_hosting
+ @domain.mx.map { |rv| self.to_symbol(rv[:host].sld) }.uniq
+ end
+ def to_symbol(name)
+ lookup = {
+ '1 & 1 internet ag' => '1and1', # 2
+ '1and1.com' => '1and1', # 1
+ 'akam.net' => 'akami', # 2
+ 'akamai technologies' => 'akami', # 1
+ 'amazon.com' => 'amazon', #13
+ 'amazon.com, inc.' => 'amazon',
+ 'comodo ca limited' => 'comodo', # 2
+ 'digicert inc' => 'digicert', # 3
+ 'domaincontrol.com' => 'godaddy', #11
+ 'dotster, inc. registrar' => '', # 2
+ 'dstr acquisition pa i, llc dba domainbank.com registrar' => '', # 1
+ 'easydns technologies, inc.' => 'easydns', # 1
+ 'easydns.com' => 'easydns', # 3
+ 'easydns.org' => 'easydns', # 1
+ 'emailsrvr.com' => 'enom', # 1
+ 'enom, inc.' => 'enom', # 7
+ 'enom, incorporated' => 'enom', # 1
+ 'equifax secure inc.' => 'equifax', # 5
+ 'gandi sas' => 'gandi', # 1
+ 'gandi.net' => 'gandi', # 2
+ 'godaddy.com' => 'godaddy', # 3
+ 'godaddy.com, inc.' => 'godaddy', #59
+ 'google.com' => 'google', #45
+ 'googlemail.com' => 'google', #44
+ 'mailhop.org' => 'dyninc', # 1
+ 'messagingengine.com' => 'enom', # 2
+ 'mydyndns.org' => 'dyninc', # 5
+ 'name-services.com' => 'enom', # 2
+ 'nettica.com' => 'nettica', # 3
+ 'network solutions, llc.' => 'netsol', # 2
+ 'pair networks' => 'pair', # 2
+ 'pair.com' => 'pair', # 2
+ 'rackspace.com' => 'rackspace', # 2
+ 'registrar-servers.com' => 'enom', # 1
+ 'secureserver.net' => 'godaddy', # 1
+ 'slicehost llc' => 'slicehost', #13
+ 'slicehost.net' => 'slicehost', #11
+ 'softlayer corporate c' => 'softlayer', # 1
+ 'softlayer technologies' => 'softlayer', # 1
+ 'softlayer.com' => 'softlayer', # 5
+ 'softlayer technologies inc.' => 'softlayer',
+ 'ultradns.info' => 'ultradns', # 1
+ 'ultradns.net' => 'ultradns', # 1
+ 'ultradns.org' => 'ultradns', # 1
+ 'verisign trust network' => 'verisign', # 1
+ 'verisign, inc.' => 'verisign', # 4
+ 'wild west domains, inc.' => 'godaddy', #1
+ }
+
+ return 'empty' unless (defined? name)
+ return 'empty' if name.nil?
+ lcname = name.downcase
+ return 'self' if lcname == @domain.host
+ return lookup[lcname] if lookup[lcname]
+ name
+ end
+ def to_text
+ self.public_methods(all=false).each { |name|
+ next if name.match(/^to_/)
+ print "#{name} = "
+ pp self.send(name.intern)
+ }
+ end
+ def to_text_simple
+ self.public_methods(all=false).each { |name|
+ next if name.match(/^to_/)
+ print self.send(name.intern).to_a.join("\n")
+ }
+ end
+end
+
+class Decision
+ def initialize(domain)
+ @domain = domain
+ end
+ def spf
+ @domain.txt.map { |record| record[:answer].gsub('"','').grep(/^v=spf/) }.flatten
+ end
+ def ttls
+ rv = {}
+ [:ns,:a,:mx,:txt].each { |type|
+ rv[type] = @domain.send(type).map { |record| seconds_to_english(record[:ttl]) }.uniq
+ }
+ rv
+ end
+ def to_text
+ self.public_methods(all=false).each { |name|
+ next if name.match(/^to_/)
+ print "#{name} = "
+ pp self.send(name.intern)
+ }
+ end
+end
+
+host = Hostname.new(ARGV[0])
+print "host = #{host.name}\n"
+domain = Domain.new(host)
+paidfor = PaidFor.new(domain)
+decision = Decision.new(domain)
+paidfor.to_text
+decision.to_text
+print "\n"
View
77 run.rb
@@ -0,0 +1,77 @@
+# encoding: UTF-8
+$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
+
+require 'pp'
+require 'domain-profile/dns'
+require 'domain-profile/whois'
+require 'domain-profile/ssl'
+require 'domain-profile/hostname'
+
+#TODO: Abstract this.
+def seconds_to_english(seconds)
+ time = [[:year,31556926], [:month,2629743], [:week,604800], [:day,86400], [:hour,3600], [:minute,60], [:second,1]]
+ seconds = seconds.to_i
+ out = []
+ time.each { |period,length|
+ count = seconds / length
+ if count > 0
+ seconds -= count * length
+
+ string = "#{count} #{period.to_s}"
+ string << 's' if count > 1
+ out.push(string)
+ end
+ }
+ out.join(', ')
+end
+
+host = ARGV[0]
+
+data = {}
+filename = "cache/#{host}.v1.cache"
+if File.exists?(filename)
+ data = open(filename) { |f| Marshal.load(f) }
+else
+ data[:dns] = `server=4.2.2.2; host=#{host}; dig @$server ns $host; dig @$server a $host; dig @$server mx $host; dig @$server txt $host`
+ data[:whois] = `whois #{host}`
+ data[:ssl] = `echo '' | openssl s_client -connect #{host}:443 2>&1`
+ open(filename, "w") { |f| Marshal.dump(data, f) }
+end
+
+def org(uri)
+ `./geoiplookup -f GeoIPOrg.dat #{uri}`.gsub("\n",'').split(': ')[1]
+end
+
+dns = DNS.new
+dns.parse(data[:dns])
+whois = Whois.new
+whois.parse(data[:whois])
+ssl = SSL.new
+ssl.parse(data[:ssl])
+
+class Array
+ def simplify(host)
+ self.map{|name| Hostname.new.simplify(name,host) }.uniq.sort
+ end
+end
+
+print "-\n"
+print " host=#{host}\n"
+print "serve="
+pp dns.a.map{|record| org(record.answer) }
+print "ttl a="
+pp dns.a.map{|record| record.ttl }.uniq.map{|s| seconds_to_english(s)}.simplify(host)
+print " dns="
+pp dns.ns.map{|record| Hostname.new.shorten(record.answer) }.simplify(host)
+print "ttlns="
+pp dns.ns.map{|record| record.ttl }.uniq.map{|s| seconds_to_english(s)}
+print " mail="
+pp dns.mx.map{|record| Hostname.new.shorten(record.host) }.simplify(host)
+print "ttlmx="
+pp dns.mx.map{|record| record.ttl }.uniq.map{|s| seconds_to_english(s)}
+print "whois="
+pp whois.registrar
+print " ssl="
+pp ssl.ca
+print " spf="
+pp dns.spf
View
4 spec/dns/dns_spec.rb
@@ -10,6 +10,10 @@
@dns.parse(data)
end
+ it "can return an SPF record" do
+ @dns.spf[0].should == 'v=spf1 include:_netblocks.google.com ~all'
+ end
+
it "knows what the 'answer' is for the NS record" do
@dns.ns[0].answer.should == 'ns1.google.com.'
end
View
60 spec/hostname/hostname_spec.rb
@@ -0,0 +1,60 @@
+# encoding: UTF-8
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
+require 'domain-profile/hostname'
+
+describe Hostname do
+
+ tests = {
+ 'dom' => 'dom',
+ 'dom.' => 'dom',
+ 'example.dom' => 'example.dom',
+ 'example.dom.' => 'example.dom',
+ 'www.example.dom' => 'example.dom',
+ 'www.example.dom' => 'example.dom',
+ 'ns1.example.dom' => 'example.dom',
+ 'ns2.example.dom' => 'example.dom',
+ 'ns1.ca.example.dom' => 'ca.example.dom',
+ }.sort
+
+ tests.each { |test|
+ k = test[0]
+ v = test[1]
+ it "correctly shortens '#{k}'" do
+ host = Hostname.new()
+ host.shorten(k).should == v
+ end
+ }
+
+ it "shortens easydns.com, easydns.net, easydns.org to 'easydns'" do
+ host = Hostname.new()
+ ["easydns.com", "easydns.net", "easydns.org"].each {|name|
+ host.simplify(name).should == 'easydns'
+ }
+ end
+
+ it "shortens EASYDNS.COM to 'easydns'" do
+ host = Hostname.new()
+ host.simplify('EASYDNS.COM').should == 'easydns'
+ end
+
+ it "shortens easydns.com to 'self' when the second option is 'easydns.net'" do
+ host = Hostname.new()
+ host.simplify('easydns.com','easydns.net').should == 'self'
+ end
+
+ it "shortens google.com to 'self' when the second option is 'google.com'" do
+ host = Hostname.new()
+ host.simplify('google.com','google.com').should == 'self'
+ end
+
+ it "correctly handles an empty string as input" do
+ host = Hostname.new()
+ host.simplify('').should == ''
+ end
+
+ it "correctly handles nil as input" do
+ host = Hostname.new()
+ host.simplify(nil).should == ''
+ end
+
+end
View
58 spec/ssl/fixtures/openssl.single.raw.dump
@@ -0,0 +1,58 @@
+depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
+verify error:num=20:unable to get local issuer certificate
+verify return:1
+depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
+verify error:num=27:certificate not trusted
+verify return:1
+depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
+verify error:num=21:unable to verify the first certificate
+verify return:1
+CONNECTED(00000003)
+---
+Certificate chain
+ 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
+ i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+---
+Server certificate
+-----BEGIN CERTIFICATE-----
+MIIDhjCCAu+gAwIBAgIQArJauA/0ymYP1R7n2/5RhTANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTA4MDMwNzE3NDU1MVoXDTEwMDQwMzIwMDUzOVow
+ZjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v
+dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJbmMxFTATBgNVBAMMDCouZ29v
+Z2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwMCKHa65sFesTGX8
+ngyOcogcgkeWC/cKl27IWJpSibl7eSxb8i7gX4Y50K04CU9y+CfDGr8W2mDTi7Lr
+K6BNgyH7IcHG6rA5YOUKAuQ4Q4euRaoVurWoy4fFJvA8v+sHAMLrvgrHK/bvhTZt
+kV8Exts9wqLvz/lKzMMOXOpOl7ECAwEAAaOByzCByDAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2NybC50aGF3
+dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwMgYIKwYBBQUHAQEEJjAk
+MCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMCMGA1UdEQQcMBqC
+Cmdvb2dsZS5jb22CDCouZ29vZ2xlLmNvbTAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3
+DQEBBQUAA4GBAJ5/HsuMsz4n9WtgZA3Dxh5gNfxFBibN3jMO73EUbMi96puRs5uL
+vqed/46imiabc5K33JIH2jTeY223rDy3faNoycZ3xFIVDeG4bTfBvxVCT8bUWZZh
+WVECHGjcrgUOSSX/zgQuE07Olxth5IkMfKWVFAHyRrw8owSvEbt036ZB
+-----END CERTIFICATE-----
+subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
+issuer=/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+---
+No client certificate CA names sent
+---
+SSL handshake has read 1056 bytes and written 304 bytes
+---
+New, TLSv1/SSLv3, Cipher is RC4-SHA
+Server public key is 1024 bit
+SSL-Session:
+ Protocol : TLSv1
+ Cipher : RC4-SHA
+ Session-ID: CC33BAF5EC92D4067EAFEE77279E346B97FBCF66F8E376CC2F23674A6FF3A4E3
+ Session-ID-ctx:
+ Master-Key: A681DF66EA72BB098A9BB2FFF8B3CA24EACF038FE314D1D68C7AE85593D46F6FFE9BFBAA10C2878EE109DCBAFF73927D
+ Key-Arg : None
+ Start Time: 1247029835
+ Timeout : 300 (sec)
+ Verify return code: 21 (unable to verify the first certificate)
+---
+DONE

0 comments on commit fc6aaaa

Please sign in to comment.
Something went wrong with that request. Please try again.