Class: EmailAddress::Exchanger

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/email_address/exchanger.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host, config = {}) ⇒ Exchanger

Returns a new instance of Exchanger.



24
25
26
27
28
# File 'lib/email_address/exchanger.rb', line 24

def initialize(host, config = {})
  @host = host
  @config = config.is_a?(Hash) ? Config.new(config) : config
  @dns_disabled = @config[:host_validation] == :syntax || @config[:dns_lookup] == :off
end

Class Method Details

.cached(host, config = {}) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/email_address/exchanger.rb', line 10

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)
  else
    @host_cache[host] = new(host, config)
  end
end

Instance Method Details

#domainsObject

Returns Array of domain names for the MX’ers, used to determine the Provider



77
78
79
# File 'lib/email_address/exchanger.rb', line 77

def domains
  @_domains ||= mxers.map { |m| Host.new(m.first).domain_name }.sort.uniq
end

#each(&block) ⇒ Object



30
31
32
33
34
35
# File 'lib/email_address/exchanger.rb', line 30

def each(&block)
  return if @dns_disabled
  mxers.each do |m|
    yield({host: m[0], ip: m[1], priority: m[2]})
  end
end

#in_cidr?(cidr) ⇒ Boolean

Given a cidr (ip/bits) and ip address, returns true on match. Caches cidr object.

Returns:

  • (Boolean)


106
107
108
109
110
# File 'lib/email_address/exchanger.rb', line 106

def in_cidr?(cidr)
  net = IPAddr.new(cidr)
  found = mx_ips.detect { |ip| net.include?(IPAddr.new(ip)) }
  !!found
end

#matches?(rules) ⇒ Boolean

Simple matcher, takes an array of CIDR addresses (ip/bits) and strings. Returns true if any MX IP matches the CIDR or host name ends in string. Ex: match?(%w(127.0.0.1/32 0:0:1/64 .yahoodns.net)) Note: Your networking stack may return IPv6 addresses instead of IPv4 when both are available. If matching on IP, be sure to include both IPv4 and IPv6 forms for matching for hosts running on IPv6 (like gmail).

Returns:

  • (Boolean)


93
94
95
96
97
98
99
100
101
102
103
# File 'lib/email_address/exchanger.rb', line 93

def matches?(rules)
  rules = Array(rules)
  rules.each do |rule|
    if rule.include?("/")
      return rule if in_cidr?(rule)
    else
      each { |mx| return rule if mx[:host].end_with?(rule) }
    end
  end
  false
end

#mx_ipsObject

Returns an array of MX IP address (String) for the given email domain



82
83
84
85
# File 'lib/email_address/exchanger.rb', line 82

def mx_ips
  return ["0.0.0.0"] if @dns_disabled
  mxers.map { |m| m[1] }
end

#mxersObject

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”



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/email_address/exchanger.rb', line 53

def mxers
  return [["example.com", "0.0.0.0", 1]] if @dns_disabled
  @mxers ||= Resolv::DNS.open { |dns|
    dns.timeouts = @config[:dns_timeout] if @config[:dns_timeout]

    ress = begin
      dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
    rescue Resolv::ResolvTimeout
      []
    end

    records = ress.map { |r|
      if r.exchange.to_s > " "
        [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference]
      end
    }
    records.compact
  }
# not found, but could also mean network not work or it could mean one record doesn't resolve an address
rescue SocketError
  [["example.com", "0.0.0.0", 1]]
end

#providerObject

Returns the provider name based on the MX-er host names, or nil if not matched



38
39
40
41
42
43
44
45
46
# File 'lib/email_address/exchanger.rb', line 38

def provider
  return @provider if defined? @provider
  Config.providers.each do |provider, config|
    if config[:exchanger_match] && matches?(config[:exchanger_match])
      return @provider = provider
    end
  end
  @provider = :default
end