Skip to content

Instantly share code, notes, and snippets.

@clicube
Created February 18, 2013 17:02
Show Gist options
  • Save clicube/4978853 to your computer and use it in GitHub Desktop.
Save clicube/4978853 to your computer and use it in GitHub Desktop.
Read TCP, UDP, IP header by Ruby
# coding: utf-8
require_relative 'protocol'
# TODO: チェックサムを確認する
class IPv4Header
attr_reader :version,:header_length,:packet_length,:identification,:frag_dont,:frag_more,:frag_offset,:ttl,:protocol,:checksum,:sndr_addr,:dest_addr,
:data_length
def initialize(packet,offset=0)
@packet = packet.force_encoding("ASCII-8BIT")
@offset = offset
header = packet.unpack("x#{offset}n10")
@version = header[0] >> 12
@header_length = ((header[0] >> 8) & 0x0f)*4
@packet_length = header[1]
@identification = header[2]
@frag_dont = (header[3] >> 14) & 0x01 != 0
@frag_more = (header[3] >> 13) & 0x01 != 0
@frag_offset = header[3] & 0x1fff
@ttl = header[4] >> 8
@protocol = header[4] & 0x00ff
@checksum = header[5]
@sndr_addr = ip_to_s(packet[12..15])
@dest_addr = ip_to_s(packet[16..19])
@data_length = @packet_length - @header_length
@virtual_header = packet[12..19] + [0,6,@data_length].pack("CCn")
end
def upper
upper_header = Protocol.to_class(@protocol)
offset = @offset+@header_length
upper_header.new(@packet,offset,@data_length,self)
end
def data
start = @offset+@header_length
@packet[start..start+@data_length]
end
def get_virtual_header
@virtual_header
end
def ip_to_s(ip)
ip = ip.unpack("n2")
sprintf("%d.%d.%d.%d",ip[0]>>8,ip[0]&0x00ff,ip[1]>>8,ip[1]&0x00ff)
end
def to_s
"IPv4 Header\n" <<
" Version : #{@version}\n" <<
" Header Length : #{@header_length}\n" <<
" Packet Length : #{@packet_length}\n" <<
" Identification : #{@identification}\n" <<
" Don't fragment : #{@frag_dont}\n" <<
" More fragments : #{@frag_more}\n" <<
" Fragment Offset : #{@frag_offset}\n" <<
" TTL : #{@ttl}\n" <<
" Protocol : #{Protocol.to_s(@protocol)}\n" <<
" Header Checksum : #{@checksum}\n" <<
" Sender Address : #{@sndr_addr}\n" <<
" Destination Address: #{@dest_addr}\n" <<
" (Data Length) : #{@data_length}"
end
end
require_relative 'ipv4header'
require_relative 'udpheader'
require_relative 'tcpheader'
class Protocol
ICMP = 0x01
IGMP = 0x02
TCP = 0x06
UDP = 0x11
IPv6 = 0x29
def self.to_class protocol
case protocol
when Protocol::ICMP
raise "ICMP is not supported"
when Protocol::IGMP
raise "IGMP is not supported"
when Protocol::TCP
TCPHeader
when Protocol::UDP
UDPHeader
when Protocol::IPv6
raise "IPv6 is not supported"
else
raise "Protocol:"+sprintf("0x%2X",protocol)+" is not supported"
end
end
def self.to_s protocol
case protocol
when Protocol::ICMP
"ICMP"
when Protocol::IGMP
"IGMP"
when Protocol::TCP
"TCP"
when Protocol::UDP
"UDP"
when Protocol::IPv6
"IPv6"
else
sprintf("0x%2X",protocol)
end
end
end
class TCPHeader
attr_reader :sndr_port,:dest_port,:seq_num,:ack_num,:header_length,
:urg,:ack,:psh,:rst,:syn,:fin,:win_size,:checksum,:emgcy_ptr,
:packet_length,:data_length,:lower
def initialize(packet,offset=0,length=nil,lower=nil)
@packet = packet.force_encoding("ASCII-8BIT")
@offset = offset
@length = length || packet.bytesize-offset
header = packet.unpack("x#{offset}n2N2n4")
@sndr_port = header[0]
@dest_port = header[1]
@seq_num = header[2]
@ack_num = header[3]
@header_length = (header[4]>>12)*4
@urg = (header[4] & 0b100000) != 0
@ack = (header[4] & 0b010000) != 0
@psh = (header[4] & 0b001000) != 0
@rst = (header[4] & 0b000100) != 0
@syn = (header[4] & 0b000010) != 0
@fin = (header[4] & 0b000001) != 0
@win_size = header[5]
@checksum = header[6]
@emgcy_ptr = header[7]
@packet_length = @length
@data_length = @packet_length-@header_length
@lower = lower
# check checksum
calc_cs = false
if calc_cs
tmp = @packet[@offset..@offset+@length]
if (tmp.length % 2) != 0
tmp += "\0"
end
data = @lower.get_virtual_header + tmp
sum = 0
list = data.unpack("n*")
list.each do |d|
sum += d
end
sum = (sum & 0xffff) + (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16)
raise if sum != 65535
end
end
def data
if(@data_length>0)
@packet[@offset+@header_length..@offset+@length]
else
""
end
end
def to_s
"TCP Header\n" <<
" Sender Port : #{@sndr_port}\n" <<
" Destination Port: #{@dest_port}\n" <<
" Sequence Number : #{@seq_num}\n" <<
" ACK Number : #{@ack_num}\n" <<
" Header Length : #{@header_length}\n" <<
" URG : #{@urg}\n" <<
" ACK : #{@ack}\n" <<
" PSH : #{@psh}\n" <<
" RST : #{@rst}\n" <<
" SYN : #{@syn}\n" <<
" FIN : #{@fin}\n" <<
" Window Size : #{@win_size}\n" <<
" Checksum : #{@checksum}\n" <<
" Emergency Ptr : #{@emgcy_ptr}\n" <<
" (Packet Length) : #{@packet_length}\n" <<
" (Data Length) : #{@data_length}"
end
end
class UDPHeader
attr_reader :sndr_port,:dist_port,:packet_length,:checksum,
:data_length,:lower
def initialize(packet,offset=0,length=nil,lower=nil)
@packet = packet
@offset = offset
header = packet.unpack("x#{offset}n4")
@sndr_port = header[0]
@dist_port = header[1]
@packet_length = header[2]
@checksum = header[3]
@data_length = @packet_length - 8
@lower = lower
end
def data
if(@packet_length>8)
@packet[@offset+8..@offset+@packet_length]
else
""
end
end
def to_s
"" <<
"UDP Header\n" <<
" Sender Port : #{@sndr_port}\n" <<
" Distication Port: #{@dist_port}\n" <<
" Packet Length : #{@packet_length}\n" <<
" Checksum : #{@checksum}\n" <<
" (Data Length) : #{@data_length}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment