219 lines
4.9 KiB
Ruby
Executable File
219 lines
4.9 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 Packet
|
|
|
|
attr_accessor :layers_list
|
|
|
|
# Constructor
|
|
def initialize(arg1, arg2)
|
|
|
|
# List of layers
|
|
@layers_list = []
|
|
|
|
# There are two cases for the arguments:
|
|
# 1) arg1 is a string to dissect and arg2 the wanted dissector
|
|
# 2) arg1 and arg2 are layers to bind together.
|
|
|
|
# First case
|
|
if arg1.is_a?(String) and arg2.is_a?(String)
|
|
|
|
# Getting the dissector from its string
|
|
dis = Scruby.get_dissector(arg2)
|
|
return if not dis
|
|
|
|
# These variables are used in the loop below.
|
|
remain = arg1
|
|
|
|
begin
|
|
# Creating a new layer and adding it to the current packet
|
|
new_layer = dis.new(remain)
|
|
@layers_list.push(new_layer)
|
|
|
|
# Preparing the remaining string for the next loop
|
|
remain = new_layer.tobedecoded
|
|
|
|
# If the upper layer was guessed by the new layer
|
|
if not new_layer.guesses[0].nil?
|
|
# In this version, only the first guess is considered.
|
|
dis = new_layer.guesses[0]
|
|
|
|
# Else, it is considered as raw data.
|
|
else
|
|
dis = Raw
|
|
end
|
|
|
|
end until remain.length == 0
|
|
|
|
# Second case
|
|
else
|
|
@layers_list = [arg1, arg2].flatten
|
|
end
|
|
end
|
|
|
|
def /(upper)
|
|
return Packet./(self, upper)
|
|
end
|
|
|
|
# Add a layer/packet/some raw data on top of a layer/packet/some raw data
|
|
def Packet./(lower, upper)
|
|
|
|
# Transforms a string into a Raw layer. This allows
|
|
# "IP()/"GET HTTP 1.0\r\n\r\n".
|
|
lower = Raw.new(:load=>lower) if lower.is_a?(String)
|
|
upper = Raw.new(:load=>upper) if upper.is_a?(String)
|
|
|
|
# Packet/Layer
|
|
if lower.instance_of?(Packet) and not upper.instance_of?(Packet)
|
|
return Packet.new(lower.layers_list, upper)
|
|
|
|
# Packet/Packet
|
|
elsif lower.instance_of?(Packet) and upper.instance_of?(Packet)
|
|
return Packet.new(lower.layers_list, upper.layers_list)
|
|
|
|
# Layer/Packet
|
|
elsif not lower.instance_of?(Packet) and upper.instance_of?(Packet)
|
|
return Packet.new(lower, upper.layers_list)
|
|
|
|
# Layer/Layer
|
|
elsif not lower.instance_of?(Packet) and not upper.instance_of?(Packet)
|
|
return Packet.new(lower, upper)
|
|
end
|
|
|
|
end
|
|
|
|
# Converts an object to a string
|
|
def to_s
|
|
|
|
out = ''
|
|
|
|
@layers_list.each do |layer|
|
|
out += layer.to_s
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
# Displays the packet with more details than tostring
|
|
def show
|
|
|
|
out = ''
|
|
|
|
@layers_list.each do |layer|
|
|
out += layer.show + "\n"
|
|
end
|
|
|
|
return out
|
|
|
|
end
|
|
|
|
# Returns the string ready to be sent on the wire
|
|
def to_net
|
|
out = ''
|
|
payload = ''
|
|
underlayer = nil
|
|
|
|
@layers_list.each do |layer|
|
|
# Only some protocols need to be aware of upper layers
|
|
if Scruby.aware_proto.include?(layer.protocol)
|
|
payload = self.get_payload(layer)
|
|
end
|
|
|
|
layer.pre_send(underlayer, payload)
|
|
out += layer.to_net()
|
|
|
|
underlayer = layer
|
|
payload = ''
|
|
end
|
|
|
|
return out
|
|
end
|
|
|
|
# Returns the payload of a layer
|
|
def get_payload(layer_arg = self)
|
|
|
|
payload = ''
|
|
concat = false
|
|
|
|
@layers_list.each do |layer|
|
|
if layer == layer_arg
|
|
concat = true
|
|
elsif concat == true
|
|
payload += layer.to_net()
|
|
end
|
|
|
|
end
|
|
|
|
return payload
|
|
end
|
|
|
|
# Return the first layer of this type with its payload
|
|
def get_layer(wanted_layer)
|
|
|
|
mylayer = nil
|
|
|
|
# Get the index of the first occurance of this layer
|
|
@layers_list.each do |layer|
|
|
|
|
if layer.class == wanted_layer
|
|
mylayer = layer
|
|
end
|
|
end
|
|
|
|
# No occurance was found
|
|
return if mylayer.nil?
|
|
|
|
# Getting the index of the wanted layer
|
|
index = @layers_list.index(mylayer)
|
|
|
|
# Returning a packet contains all layers, beginning at the wanted layer
|
|
return Packet.new(@layers_list[index..-1], nil)
|
|
end
|
|
|
|
# Return the first layer of this type with its payload
|
|
# Differs from get_layer() in that it returns the layer not the packet object
|
|
def layer(wanted_layer)
|
|
ret = get_layer(wanted_layer)
|
|
ret.layers_list[0]
|
|
end
|
|
|
|
# Checks wether the packet has a given layer
|
|
def has_layer(wanted_layer)
|
|
return (not self.get_layer(wanted_layer).nil?)
|
|
end
|
|
|
|
# Returns the last layer of the packet
|
|
def last_layer
|
|
return @layers_list[-1]
|
|
end
|
|
|
|
# Decode the raw data with the given dissector
|
|
def decode_payload_as(dissector)
|
|
last = self.last_layer
|
|
|
|
# Applying this function doesn't make sense if Raw isn't the last layer
|
|
return if last.class.to_s.split('::')[1] != 'Raw'
|
|
|
|
# Building a new packet from the Raw payload with the given dissector
|
|
p = Packet.new(last.load, dissector.to_s.split('::')[1])
|
|
|
|
# Removing the Raw layer from the original packet
|
|
@layers_list.pop
|
|
|
|
# Binding the new packet over the original packet
|
|
@layers_list.concat(p.layers_list)
|
|
end
|
|
|
|
end
|
|
end
|