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

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.



227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/spf/model.rb', line 227

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



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

def default_ipv4_prefix_length; 32;   end

#default_ipv6_prefix_lengthObject



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

def default_ipv6_prefix_length; 128;  end

#explain(server, request, result) ⇒ Object



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/spf/model.rb', line 323

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



339
340
341
# File 'lib/spf/model.rb', line 339

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

#match_in_domain(server, request, domain) ⇒ Object



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/spf/model.rb', line 298

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
  packet             = server.dns_lookup(domain, 'ANY')
  server.count_void_dns_lookup(request) unless (rrs = packet)

  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

#parseObject



240
241
242
243
244
245
246
247
248
# File 'lib/spf/model.rb', line 240

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

#parse_endObject



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

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

#parse_nameObject



259
260
261
262
263
264
265
# File 'lib/spf/model.rb', line 259

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

#parse_params(required = true) ⇒ Object



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

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

#parse_qualifierObject



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

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

#qualifierObject



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

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

#to_sObject



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

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

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