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.



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

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



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

def default_ipv4_prefix_length; 32;   end

#default_ipv6_prefix_lengthObject



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

def default_ipv6_prefix_length; 128;  end

#explain(server, request, result) ⇒ Object



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

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



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

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

#match_in_domain(server, request, domain) ⇒ Object



324
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
# File 'lib/spf/model.rb', line 324

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



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

def params; nil; end

#parseObject



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

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



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

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



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

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



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

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



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

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



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

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

#to_sObject



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

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

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