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, #record_domain

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.



253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/spf/model.rb', line 253

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



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

def default_ipv4_prefix_length; 32;   end

#default_ipv6_prefix_lengthObject



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

def default_ipv6_prefix_length; 128;  end

#explain(server, request, result) ⇒ Object



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/spf/model.rb', line 367

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



383
384
385
# File 'lib/spf/model.rb', line 383

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

#match_in_domain(server, request, domain) ⇒ Object



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
363
364
365
# File 'lib/spf/model.rb', line 328

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



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

def params; nil; end

#parseObject



266
267
268
269
270
271
272
273
274
# File 'lib/spf/model.rb', line 266

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



304
305
306
307
308
309
# File 'lib/spf/model.rb', line 304

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



287
288
289
290
291
292
293
# File 'lib/spf/model.rb', line 287

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



295
296
297
298
299
300
301
302
# File 'lib/spf/model.rb', line 295

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



278
279
280
281
282
283
284
285
# File 'lib/spf/model.rb', line 278

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



311
312
313
314
315
# File 'lib/spf/model.rb', line 311

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

#to_sObject



317
318
319
320
321
322
323
324
325
326
# File 'lib/spf/model.rb', line 317

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

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