Class: SPF::Mech

Inherits:
Term
  • Object
show all
Defined in:
lib/spf/model.rb

Direct Known Subclasses

A, All, Exists, IP4, IP6, Include, MX, PTR

Defined Under Namespace

Classes: A, All, Exists, IP4, IP6, Include, MX, PTR

Constant Summary collapse

DEFAULT_QUALIFIER =
SPF::Record::DEFAULT_QUALIFIER
QUALIFIER_PATTERN =
'[+\\-~\\?]'
NAME_PATTERN =
"#{NAME_PATTERN} (?= [:\\/\\x20] | $ )"
EXPLANATION_TEMPLATES_BY_RESULT_CODE =
{
  :pass     => "Sender is authorized to use '%{s}' in '%{_scope}' identity",
  :fail     => "Sender is not authorized to use '%{s}' in '%{_scope}' identity",
  :softfail => "Sender is not authorized to use '%{s}' in '%{_scope}' identity, however domain is not currently prepared for false failures",
  :neutral  => "Domain does not state whether sender is authorized to use '%{s}' in '%{_scope}' identity"
}

Constants inherited from Term

Term::DOMAIN_END_PATTERN, Term::DOMAIN_SPEC_PATTERN, Term::HEXWORD_PATTERN, Term::IPV4_ADDRESS_PATTERN, Term::IPV6_ADDRESS_PATTERN, Term::MACRO_DELIMITER, Term::MACRO_EXPAND_PATTERN, Term::MACRO_LITERAL_PATTERN, Term::MACRO_STRING_PATTERN, Term::MACRO_TRANSFORMERS_PATTERN, Term::QNUM_PATTERN, Term::TOPLABEL_PATTERN, Term::TWO_HEXWORDS_OR_IPV4_ADDRESS_PATTERN

Instance Attribute Summary

Attributes inherited from Term

#domain_spec, #errors, #ip_address, #ip_netblocks, #ip_network, #ipv4_prefix_length, #ipv6_prefix_length, #raw_params

Instance Method Summary collapse

Methods inherited from Term

#domain, #error, new_from_string, #parse_domain_spec, #parse_ipv4_address, #parse_ipv4_ipv6_prefix_lengths, #parse_ipv4_network, #parse_ipv4_prefix_length, #parse_ipv6_address, #parse_ipv6_network, #parse_ipv6_prefix_length, #text

Constructor Details

#initialize(options) ⇒ Mech

Returns a new instance of Mech.



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/spf/model.rb', line 250

def initialize(options)
  super(options)

  @text = options[:text]
  if not self.instance_variable_defined?(:@parse_text)
    @parse_text = @text.dup
  end
  if self.instance_variable_defined?(:@domain_spec) and
    not SPF::MacroString === @domain_spec
    @domain_spec = SPF::MacroString.new({:text => @domain_spec})
  end
end

Instance Method Details

#default_ipv4_prefix_lengthObject



237
# File 'lib/spf/model.rb', line 237

def default_ipv4_prefix_length; 32;   end

#default_ipv6_prefix_lengthObject



238
# File 'lib/spf/model.rb', line 238

def default_ipv6_prefix_length; 128;  end

#explain(server, request, result) ⇒ Object



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/spf/model.rb', line 364

def explain(server, request, result)
  explanation_template = self.explanation_template(server, request, result)
  return unless explanation_template
  begin
    explanation = SPF::MacroString.new({
      :text           => explanation_template,
      :server         => server,
      :request        => request,
      :is_explanation => true
    })
    request.state(:local_explanation, explanation)
  rescue SPF::Error
  rescue SPF::Result
  end
end

#explanation_template(server, request, result) ⇒ Object



380
381
382
# File 'lib/spf/model.rb', line 380

def explanation_template(server, request, result)
  return EXPLANATION_TEMPLATES_BY_RESULT_CODE[result.code]
end

#match_in_domain(server, request, domain) ⇒ Object



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/spf/model.rb', line 325

def match_in_domain(server, request, domain)
  domain = self.domain(server, request) unless domain

  ipv4_prefix_length = @ipv4_prefix_length || self.default_ipv4_prefix_length
  ipv6_prefix_length = @ipv6_prefix_length || self.default_ipv6_prefix_length

  begin
    rrs_a = server.dns_lookup(domain.to_s, 'A') || []
  rescue SPF::DNSError => e
    @errors << e
    return false
  end
  begin
    rrs_aaaa = server.dns_lookup(domain.to_s, 'AAAA') || []
  rescue SPF::DNSError => e
    @errors << e
    return false
  end

  rrs = rrs_a + rrs_aaaa
  server.count_void_dns_lookup(request) if rrs.empty?

  rrs.each do |rr|
    if Resolv::DNS::Resource::IN::A === rr
      network = IP.new("#{rr.address}/#{ipv4_prefix_length}")
      @ip_netblocks << network
      return true if network.contains?(request.ip_address)
    elsif Resolv::DNS::Resource::IN::AAAA === rr
      network = IP.new("#{rr.address}/#{ipv6_prefix_length}")
      @ip_netblocks << network
      return true if network.contains?(request.ip_address_v6)
    else
      # Unexpected RR type.
      # TODO: Generate debug info or ignore silently.
    end
  end
  return false
end

#paramsObject



273
# File 'lib/spf/model.rb', line 273

def params; nil; end

#parseObject



263
264
265
266
267
268
269
270
271
# File 'lib/spf/model.rb', line 263

def parse
  if not @parse_text
    raise SPF::NothingToParseError.new('Nothing to parse for mechanism')
  end
  parse_qualifier
  parse_name      if @errors.empty?
  parse_params    if @errors.empty?
  parse_end       if @errors.empty?
end

#parse_endObject



301
302
303
304
305
306
# File 'lib/spf/model.rb', line 301

def parse_end
  unless @parse_text == ''
    error(SPF::JunkInTermError.new("Junk encountered in mechanism '#{@text}'", @text, @parse_text))
  end
  @parse_text = nil
end

#parse_nameObject



284
285
286
287
288
289
290
# File 'lib/spf/model.rb', line 284

def parse_name
  if @parse_text.sub!(/^ (#{NAME_PATTERN}) (?: : (?=.) )? /x, '')
    @name = $1
  else
    error(SPF::InvalidMechError.new("Unexpected mechanism encountered in '#{@text}'"))
  end
end

#parse_params(required = true) ⇒ Object



292
293
294
295
296
297
298
299
# File 'lib/spf/model.rb', line 292

def parse_params(required = true)
  @raw_params = @parse_text.dup
  # Parse generic string of parameters text (should be overridden in sub-classes):
  if @parse_text.sub!(/^(.*)/, '')
    @params_text = $1
    @raw_params  = @params_text.dup
  end
end

#parse_qualifierObject



275
276
277
278
279
280
281
282
# File 'lib/spf/model.rb', line 275

def parse_qualifier
  if @parse_text.sub!(/(#{QUALIFIER_PATTERN})?/x, '')
    @qualifier = $1 or DEFAULT_QUALIFIER
  else
    error(SPF::InvalidMechQualifierError.new(
        "Invalid qualifier encountered in '#{@text}'"))
  end
end

#qualifierObject



308
309
310
311
312
# File 'lib/spf/model.rb', line 308

def qualifier
  # Read-only!
  return @qualifier if self.instance_variable_defined?(:@qualifier) and @qualifier
  return DEFAULT_QUALIFIER
end

#to_sObject



314
315
316
317
318
319
320
321
322
323
# File 'lib/spf/model.rb', line 314

def to_s
  @params = nil unless self.instance_variable_defined?(:@params)

  return sprintf(
    '%s%s%s',
    @qualifier == DEFAULT_QUALIFIER ? '' : @qualifier,
    @name,
    @params ? @params : ''
  )
end