Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MX validation behaves inconsistently depending on network connectivity #43

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions lib/email_address/exchanger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ def self.cached(host, config={})
@host_cache ||= {}
@cache_size ||= ENV['EMAIL_ADDRESS_CACHE_SIZE'].to_i || 100
if @host_cache.has_key?(host)
o = @host_cache.delete(host)
@host_cache[host] = o # LRU cache, move to end
elsif @host_cache.size >= @cache_size
@host_cache.delete(@host_cache.keys.first)
@host_cache[host] = new(host, config)
exchanger = @host_cache.delete(host)
exchanger = new(host, config) if exchanger.network_was_down?
@host_cache[host] = exchanger # LRU cache, move to end
else
@host_cache[host] = new(host, config)
if @host_cache.size >= @cache_size
@host_cache.delete(@host_cache.keys.first)
end
exchanger = @host_cache[host] = new(host, config)
end
exchanger
end

def initialize(host, config={})
@host = host
@config = config
@network_down_at = nil
end

def each(&block)
Expand All @@ -44,13 +47,41 @@ def provider
@provider = :default
end

# Returns: [official_hostname, alias_hostnames, address_family, *address_list]
def a_record
@_a_record = [@host, [], 2, ""] if @config[:dns_lookup] == :off
@_a_record ||= Socket.gethostbyname(@host)
rescue SocketError # not found, but could also mean network not work
if network_down?
@_a_record = [@host, [], 2, ""]
else
@_a_record ||= []
end
end

def network_down?
return false if @config[:dns_lookup] == :off
return false if @config[:dns_unavailable] == :ignore

Socket.gethostbyname('example.com') # Should always exist
@network_down_at = nil
false
rescue SocketError # DNS Failed, so network is down
@network_down_at = Time.new
true
end

def network_was_down?
@network_down_at ? true : false
end

# Returns: [["mta7.am0.yahoodns.net", "66.94.237.139", 1], ["mta5.am0.yahoodns.net", "67.195.168.230", 1], ["mta6.am0.yahoodns.net", "98.139.54.60", 1]]
# If not found, returns []
# Returns a dummy record when dns_lookup is turned off since it may exists, though
# may not find provider by MX name or IP. I'm not sure about the "0.0.0.0" ip, it should
# be good in this context, but in "listen" context it means "all bound IP's"
def mxers
return [["example.com", "0.0.0.0", 1]] if @config[:dns_lookup] == :off
return [[@host, "0.0.0.0", 1]] if @config[:dns_lookup] == :off
@mxers ||= Resolv::DNS.open do |dns|
dns.timeouts = @config[:dns_timeout] if @config[:dns_timeout]

Expand All @@ -59,6 +90,7 @@ def mxers
rescue Resolv::ResolvTimeout
[]
end
return [[@host, "0.0.0.0", 1]] if ress.empty? && network_down?

records = ress.map do |r|
begin
Expand All @@ -67,8 +99,12 @@ def mxers
else
nil
end
rescue SocketError # not found, but could also mean network not work or it could mean one record doesn't resolve an address
nil
rescue SocketError
if network_down?
[@host, "0.0.0.0", 1]
else # Not Found
nil
end
end
end
records.compact
Expand Down
9 changes: 4 additions & 5 deletions lib/email_address/host.rb
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,11 @@ def dns_enabled?
[:mx, :a].include?(EmailAddress::Config.setting(:host_validation))
end

# Returns: [official_hostname, alias_hostnames, address_family, *address_list]
# Returns: [official_hostname, [alias'], address_family, *address_list]
def dns_a_record
@_dns_a_record = "0.0.0.0" if @config[:dns_lookup] == :off
@_dns_a_record ||= Socket.gethostbyname(self.dns_name)
rescue SocketError # not found, but could also mean network not work
@_dns_a_record ||= []
return [self.dns_name, [], 2, ""] if @config[:dns_lookup] == :off

@_dns_a_record = exchangers.a_record
end

# Returns an array of EmailAddress::Exchanger hosts configured in DNS.
Expand Down