class EmailAddress::Exchanger

Public Class Methods

cached(host, config = {}) click to toggle source
# 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
new(host, config = {}) click to toggle source
# 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

Public Instance Methods

domains() click to toggle source

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

# File lib/email_address/exchanger.rb, line 77
def domains
  @_domains ||= mxers.map { |m| Host.new(m.first).domain_name }.sort.uniq
end
each() { |{host: m, ip: m, priority: m}| ... } click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source

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).

# 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_ips() click to toggle source

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

# 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
mxers() click to toggle source

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”

# 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
provider() click to toggle source

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

# 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