spf_lookup 0.1.2a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/README.md +89 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/spf_lookup/error.rb +10 -0
- data/lib/spf_lookup/lookup.rb +62 -0
- data/lib/spf_lookup/spf_record.rb +51 -0
- data/lib/spf_lookup/txt_record_fetcher.rb +33 -0
- data/lib/spf_lookup/version.rb +3 -0
- data/lib/spf_lookup.rb +28 -0
- data/spf_lookup.gemspec +46 -0
- metadata +142 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 73fb8d746701c15855dfaed242f391e384ce96071157335c76b9bb73ce7e01f0
|
4
|
+
data.tar.gz: 17af222e4787d6c07f0778485912a86138b5671cc81597e697e56dd3a7b3cc3d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4fda7cf88816bb58a9ce8a474c705f98902f6885f588c2b3a576ce45ffec69ca20d05aea0bcacc59b63b6a689fdc41a4dfd22bc18ad3536420d1bb24aceda65
|
7
|
+
data.tar.gz: fbe13fccbda702dc559ca355b3fc7ecf0ef58323cc2ebbea180c51f29ba7d85f7d729b7f70c9fd5c49ea7c50b9ebbd202a26f14e20a0b864403f2f666b310139
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# SpfLookup
|
2
|
+
|
3
|
+
SpfLookup will count the number of SPF lookups for a given domain.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
RFC specify a limit of 10 DNS lookups for SPF. https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4
|
8
|
+
|
9
|
+
Therefore, when configuring SPF, you need to check the number of SPF lookups for target domain and make sure that the number of lookups after configuration is within the upper limit set by the RFC.
|
10
|
+
|
11
|
+
SpfLookup will count the nubmer of SPF lookups by passing the domain,
|
12
|
+
so you can reduce the work of counting the number of SPF lookups.
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'spf_lookup'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install spf_lookup
|
31
|
+
|
32
|
+
## Example
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require "spf_lookup"
|
36
|
+
|
37
|
+
domain = "sample.example.com"
|
38
|
+
|
39
|
+
resolv_conf = {
|
40
|
+
nameserver: ['8.8.8.8']
|
41
|
+
}
|
42
|
+
|
43
|
+
# SpfLookup.dns_configure argumets is the same as the one passed to the initializer of Resolv::DNS
|
44
|
+
SpfLookup.dns_configure(resolv_conf)
|
45
|
+
|
46
|
+
|
47
|
+
# Retrive spf record set.
|
48
|
+
record_set = SpfLookup.retrieve_record_set(domain)
|
49
|
+
record_set_hash = record_set.to_hash
|
50
|
+
|
51
|
+
## record set information
|
52
|
+
puts record_set_hash[:lookup_term_count]
|
53
|
+
puts record_set_hash[:domain]
|
54
|
+
puts record_set_hash[:record_value]
|
55
|
+
puts record_set_hash[:includes]
|
56
|
+
|
57
|
+
puts record_set_hash[:includes].first[:lookup_term_count]
|
58
|
+
puts record_set_hash[:includes].first[:domain]
|
59
|
+
puts record_set_hash[:includes].first[:record_value]
|
60
|
+
puts record_set_hash[:includes].first[:includes]
|
61
|
+
|
62
|
+
# Lookup count check.
|
63
|
+
## If you want to use "retrieve_record_set" method, use this.
|
64
|
+
lookup_count = record_set.lookup_count
|
65
|
+
## If you DON'T want to use "retrieve_record_set" method, use this.
|
66
|
+
lookup_count = SpfLookup.lookup_count(domain)
|
67
|
+
|
68
|
+
if lookup_count > SpfLookup::LOOKUP_LIMIT_SPECIFIED_BY_RFC7208
|
69
|
+
puts "Invalid SPF lookup count!"
|
70
|
+
end
|
71
|
+
|
72
|
+
```
|
73
|
+
|
74
|
+
## Development
|
75
|
+
|
76
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
77
|
+
|
78
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
79
|
+
|
80
|
+
## Contributing
|
81
|
+
|
82
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/shirot7335/spf_lookup. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
83
|
+
|
84
|
+
## WIP
|
85
|
+
* test
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "spf_lookup"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'coppertone'
|
2
|
+
require_relative './txt_record_fetcher'
|
3
|
+
require_relative './spf_record'
|
4
|
+
require_relative './error'
|
5
|
+
|
6
|
+
module SpfLookup
|
7
|
+
class Lookup
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def run(domain)
|
11
|
+
return dns_lookup(domain)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def dns_lookup(domain)
|
17
|
+
spf_record = find_spf_record(domain)
|
18
|
+
|
19
|
+
includes = lookup_target_domains(spf_record).map {|_domain|
|
20
|
+
dns_lookup(_domain)
|
21
|
+
}
|
22
|
+
|
23
|
+
return SpfRecord.new(
|
24
|
+
domain,
|
25
|
+
spf_record&.to_s,
|
26
|
+
includes,
|
27
|
+
spf_record&.dns_lookup_term_count
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def lookup_target_domains(spf_record)
|
32
|
+
return [] if spf_record.blank?
|
33
|
+
|
34
|
+
domain_fetcher = -> (obj) { obj.domain_spec.macro_text }
|
35
|
+
|
36
|
+
domains = spf_record.includes.each_with_object([]) { |include_value, memo|
|
37
|
+
memo << domain_fetcher.(include_value.mechanism)
|
38
|
+
}
|
39
|
+
domains << domain_fetcher.(spf_record.redirect) unless spf_record.redirect.nil?
|
40
|
+
|
41
|
+
return domains.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_spf_record(domain)
|
45
|
+
spf_record = record_fetcher.txt_record_values(domain).each_with_object([]) {|txt_record_value, memo|
|
46
|
+
memo << Coppertone::Record.new(txt_record_value) if Coppertone::Record.record?(txt_record_value)
|
47
|
+
}
|
48
|
+
|
49
|
+
if spf_record.length > 1
|
50
|
+
raise SpfLookup::MultipleSpfRecordError.new("There must be only one SPF record in the same domain.")
|
51
|
+
end
|
52
|
+
|
53
|
+
return spf_record.first
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_fetcher
|
57
|
+
@fetcher ||= TXTRecordFetcher.new(SpfLookup::DNS_CONFIG[:option])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module SpfLookup
|
4
|
+
class SpfRecord
|
5
|
+
attr_accessor :domain, :record_value, :includes
|
6
|
+
attr_accessor :lookup_term_count
|
7
|
+
|
8
|
+
def initialize(domain, record_value, includes, lookup_term_count)
|
9
|
+
@domain = domain || ""
|
10
|
+
@record_value = record_value || ""
|
11
|
+
@includes = includes || []
|
12
|
+
@lookup_term_count = lookup_term_count || 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def lookup_count
|
16
|
+
return count_dns_lookup_0
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
return {
|
21
|
+
domain: @domain,
|
22
|
+
record_value: @record_value,
|
23
|
+
includes: includes_to_hash,
|
24
|
+
lookup_term_count: @lookup_term_count
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_json
|
29
|
+
return self.to_hash.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def count_dns_lookup_0
|
35
|
+
return count_dns_lookup(self, 0)
|
36
|
+
end
|
37
|
+
|
38
|
+
def count_dns_lookup(result, count)
|
39
|
+
_count = result.includes.inject(count) {|memo, r|
|
40
|
+
memo = count_dns_lookup(r, memo)
|
41
|
+
}
|
42
|
+
|
43
|
+
return _count + result.lookup_term_count
|
44
|
+
end
|
45
|
+
|
46
|
+
def includes_to_hash
|
47
|
+
return includes.map {|result| result.to_hash }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
|
4
|
+
module SpfLookup
|
5
|
+
class TXTRecordFetcher
|
6
|
+
|
7
|
+
SUPPORTED_TYPE_CLASS = %w[TXT].freeze
|
8
|
+
|
9
|
+
def initialize(dns_conf = nil)
|
10
|
+
@resolver = Resolv::DNS.new(dns_conf)
|
11
|
+
end
|
12
|
+
|
13
|
+
def txt_record_values(domain)
|
14
|
+
return txt_record_resources(domain).collect { |resource| resource.data }
|
15
|
+
end
|
16
|
+
|
17
|
+
def txt_record_resources(domain)
|
18
|
+
return record_resources(domain, 'TXT')
|
19
|
+
end
|
20
|
+
|
21
|
+
def record_resources(domain, record_type)
|
22
|
+
resources = @resolver.getresources(domain, type_class(record_type))
|
23
|
+
return resources
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def type_class(record_type)
|
28
|
+
raise ArgumentError unless SUPPORTED_TYPE_CLASS.include?(record_type.upcase)
|
29
|
+
Resolv::DNS::Resource::IN.const_get(record_type.upcase)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/spf_lookup.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spf_lookup/version"
|
2
|
+
|
3
|
+
require_relative './spf_lookup/lookup'
|
4
|
+
|
5
|
+
module SpfLookup
|
6
|
+
|
7
|
+
# 'options' could set nil or Resolv::DNS.new argument.
|
8
|
+
# ex.
|
9
|
+
# {nameserver: '8.8.8.8'}
|
10
|
+
DNS_CONFIG = {option: nil}
|
11
|
+
LOOKUP_LIMIT_SPECIFIED_BY_RFC7208 = 10
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def retrieve_record_set(domain)
|
15
|
+
return SpfLookup::Lookup.run(domain)
|
16
|
+
end
|
17
|
+
|
18
|
+
def lookup_count(domain)
|
19
|
+
return SpfLookup::Lookup.run(domain).lookup_count
|
20
|
+
end
|
21
|
+
|
22
|
+
def dns_configure(dns_config = nil)
|
23
|
+
DNS_CONFIG[:option] = dns_config
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
data/spf_lookup.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "spf_lookup/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "spf_lookup"
|
8
|
+
spec.version = SpfLookup::VERSION
|
9
|
+
spec.authors = ["Ryouichi Suganuma"]
|
10
|
+
spec.email = ["shirot.white4624@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{lookup counter for SPF registration.}
|
13
|
+
spec.description = %q{SpfLookup will count the number of SPF lookups for a given domain.}
|
14
|
+
spec.homepage = "https://github.com/shirot7335/spf_lookup"
|
15
|
+
spec.license = "Confidential"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
#if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
|
22
|
+
# spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
# spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
24
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
25
|
+
#else
|
26
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
# "public gem pushes."
|
28
|
+
#end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
|
39
|
+
spec.add_dependency "domain_name", "~> 0.5"
|
40
|
+
spec.add_dependency "coppertone", "~> 0.0.10"
|
41
|
+
|
42
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
43
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
44
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
45
|
+
spec.add_development_dependency "pry", "~> 0.12.2"
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spf_lookup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2a
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryouichi Suganuma
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: domain_name
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coppertone
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.10
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.10
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.12.2
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.12.2
|
97
|
+
description: SpfLookup will count the number of SPF lookups for a given domain.
|
98
|
+
email:
|
99
|
+
- shirot.white4624@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- bin/console
|
111
|
+
- bin/setup
|
112
|
+
- lib/spf_lookup.rb
|
113
|
+
- lib/spf_lookup/error.rb
|
114
|
+
- lib/spf_lookup/lookup.rb
|
115
|
+
- lib/spf_lookup/spf_record.rb
|
116
|
+
- lib/spf_lookup/txt_record_fetcher.rb
|
117
|
+
- lib/spf_lookup/version.rb
|
118
|
+
- spf_lookup.gemspec
|
119
|
+
homepage: https://github.com/shirot7335/spf_lookup
|
120
|
+
licenses:
|
121
|
+
- Confidential
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: 1.3.1
|
137
|
+
requirements: []
|
138
|
+
rubygems_version: 3.0.3
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: lookup counter for SPF registration.
|
142
|
+
test_files: []
|