201 lines
5.2 KiB
Ruby
Executable File
201 lines
5.2 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# Copyright (C) 2007 Sylvain SARMEJEANNE
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; version 2.
|
|
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
|
|
module Scruby
|
|
class Layer
|
|
|
|
attr_accessor :protocol
|
|
attr_accessor :fields_desc
|
|
attr_accessor :tobedecoded
|
|
attr_accessor :guesses
|
|
|
|
# Constructor
|
|
def initialize(args = {})
|
|
|
|
@protocol = 'Generic layer'
|
|
|
|
# Array containing the protocol fields (see dissectors.rb)
|
|
@fields_desc = []
|
|
|
|
# Part of the string that couldn't be decoded, to be passed to the
|
|
# upper layer
|
|
@tobedecoded = nil
|
|
|
|
# Guesses for the upper layer
|
|
@guesses = []
|
|
|
|
# Cleaning arguments
|
|
if not args.is_a?(Hash) and not args.is_a?(String)
|
|
args = {}
|
|
end
|
|
|
|
# Constructing the layer
|
|
init()
|
|
|
|
# If a single string argument is passed (e.g. "Ether('string')")
|
|
if args.is_a?(String)
|
|
|
|
# The default values are applied.
|
|
@fields_desc.each do |field|
|
|
self.instance_variable_set("@#{field.name}", field.default_value)
|
|
end
|
|
|
|
# The values for this layer are retrieved from the beginning of
|
|
# the string. dissect returns the end of the string,
|
|
# that couldn't be decoded.
|
|
|
|
@tobedecoded = dissect(args)
|
|
|
|
# If there is something left to be decoded
|
|
if @tobedecoded.length > 0
|
|
# layer_bounds is run throught to try to guess the upper layer.
|
|
# There can be several answers (array @guesses).
|
|
myclass = self.class.to_s.split('::')[1]
|
|
proto_array = Scruby.layer_bounds.has_key?(myclass) ? Scruby.layer_bounds[myclass] : []
|
|
|
|
proto_array.each do |triplet|
|
|
# Value from the layer_bounds triplet and the real one are compared.
|
|
# e.g. for ['type', ETHERTYPE_IPv4, IP], if the field "type"
|
|
# in the current Ethernet layer is 0x800, then the upper layer
|
|
# is (may be) IP.
|
|
if triplet[0] == BIND_ALWAYS or self.instance_variable_get("@#{triplet[0]}") == triplet[1]
|
|
# Adding this possibility
|
|
@guesses.push(triplet[2])
|
|
break
|
|
end
|
|
end
|
|
end
|
|
else
|
|
# At this point, args is a hash.
|
|
# Adding the field values, overwriting the default values if the
|
|
# user specified some.
|
|
# There is no verification of the validity of the arguments passed;
|
|
# that is to say something like "IP(:foo=>'bar')" will not
|
|
# display any error (the argument will just be ignored).
|
|
|
|
# Converting symbols to strings
|
|
args.each_key do |symbol|
|
|
args[symbol.to_s] = args[symbol]
|
|
end
|
|
|
|
@fields_desc.each do |field|
|
|
# Setting the variable value
|
|
value = args.has_key?(field.name) ? args[field.name] : field.default_value
|
|
self.instance_variable_set("@#{field.name}", field.from_human(value))
|
|
end
|
|
end
|
|
end
|
|
|
|
# Layer initialization. This function have to be redefined by subclasses.
|
|
def init
|
|
end
|
|
|
|
# Redefines the "/" operator (allows "p=IP()/TCP()").
|
|
def /(upper)
|
|
return Packet./(self, upper)
|
|
end
|
|
|
|
# To use 'MyField(foo, bar)' in dissectors, instead of Scruby.MyField(foo, bar)'
|
|
def method_missing(method, *args)
|
|
return Scruby.field(method, *args)
|
|
end
|
|
|
|
# Converts an object to a string
|
|
def to_s
|
|
|
|
# Name of the protocol
|
|
out = "<#{self.class.to_s.split('::')[1]}"
|
|
|
|
# Only the fields whose values are not the default ones will be displayed.
|
|
@fields_desc.each do |field|
|
|
if self.instance_variable_get("@#{field.name}") != field.default_value
|
|
out += " #{field.name}="
|
|
# to_human returns less information than to_human_complete
|
|
# e.g. "6" instead of "6 (TCP)" for IP protocol
|
|
out += field.to_human(self.instance_variable_get("@#{field.name}"))
|
|
end
|
|
end
|
|
|
|
return out += ' |>'
|
|
end
|
|
|
|
# Displays the packet with more details than to_s
|
|
def show
|
|
|
|
# Name of the protocol
|
|
out = "###[ #{@protocol} ]###"
|
|
|
|
# List of fields in this layer
|
|
@fields_desc.each do |field|
|
|
# to_human_complete returns more information then to_human
|
|
# e.g. "6 (TCP)" instead of "6" for IP protocol
|
|
out += "\n#{field.name} = " + field.to_human_complete(self.instance_variable_get("@#{field.name}"))
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
# Returns the string ready to be sent on the wire
|
|
def to_net
|
|
out = ''
|
|
|
|
@fields_desc.each do |field|
|
|
if field.is_applicable?(self)
|
|
out += field.to_net(self.instance_variable_get("@#{field.name}"))
|
|
end
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
# Finishes the packet just before sending it (checksum, etc).
|
|
# This function may be redefined by subclasses.
|
|
def pre_send(underlayer = nil, payload = nil)
|
|
end
|
|
|
|
# Retrieves field values from a string and returns what was not decoded
|
|
def dissect(string)
|
|
|
|
@fields_desc.each do |field|
|
|
if field.is_applicable?(self)
|
|
string = field.dissect(self, string)
|
|
end
|
|
return '' if string.nil?
|
|
end
|
|
|
|
return string
|
|
end
|
|
|
|
# Computes the checksum of a string
|
|
def Layer.checksum(string)
|
|
|
|
s = 0
|
|
i = 0
|
|
|
|
# Adding a null character if needed
|
|
if string.length % 2 != 0
|
|
string += 0.chr
|
|
end
|
|
|
|
while i < (string.length)/2
|
|
s += string[2*i, 2].unpack('n')[0]
|
|
i += 1
|
|
end
|
|
|
|
s = (s >> 16) + (s & 0xffff)
|
|
s = ~((s >> 16) + s) & 0xffff
|
|
|
|
return s
|
|
end
|
|
|
|
end
|
|
end |