require 'socket' require 'ipaddr' class RawSocket # :nodoc: @@id_arr = [] def initialize(src_addr,dest_addr) # Define socket begin @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW rescue SystemCallError => e raise SystemCallError, "You must be root to use raw sockets! #{e}" end @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1 # Checks addresses @src_addr = check_addr src_addr @dest_addr = check_addr dest_addr # Source and destination port are zero @src_port = 0 @dest_port = 0 # Set correct protocol version in the header @version = @dest_addr.ipv4? ? "0100" : "0110" # Total lenght: must be overridden by subclasses @tot_lenght = 20 # Protocol: must be overridden by subclasses @protocol = 1 # ICMP by default # Generate a new id # @id = genID @id = 1234 # Generate peer sockaddr @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end def send(payload = '') packet = make_ip_header([[ @version+'0101', 'B8' ], # version, hlen [ 0, 'C' ], # tos [ @tot_lenght + payload.size, 'n' ], # total len [ @id, 'n' ], # id [ 0, 'n' ], # flags, offset [ 64, 'C' ], # ttl [ @protocol, 'C' ], # protocol [ 0, 'n' ], # checksum [ @src_addr.to_i, 'N' ], # source [ @dest_addr.to_i, 'N' ], # destination ]) packet << make_transport_header(payload.size) packet << [payload].pack("a*") @socket.send(packet,0,@to) end private def check_addr addr case addr when String IPAddr.new addr when IPAddr addr else raise ArgumentError, "Wrong address format: #{addr}" end end def check_port port if (1..65535).include? port and port.kind_of? Integer port else raise ArgumentError, "Port #{port} not valid" end end def genID while (@@id_arr.include?(q = rand(65535))) end @@id_arr.push(q) q end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_transport_header "" end end class UdpRawSocket < RawSocket # :nodoc: def initialize(src_addr,src_port,dest_addr,dest_port) super(src_addr,dest_addr) # Check ports @src_port = check_port src_port @dest_port = check_port dest_port # Total lenght: must be overridden by subclasses @tot_lenght = 20 + 8 # 8 bytes => UDP Header # Protocol: must be overridden by subclasses @protocol = 17 # UDP protocol @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end private def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end def make_transport_header(pay_size) make_udp_header([ [ @src_port, 'n'], # source port [ @dest_port, 'n' ], # destination port [ 8 + pay_size, 'n' ], # len [ 0, 'n' ] # checksum (mandatory) ]) end end