Skip to content

Instantly share code, notes, and snippets.

@chintanparikh
Created April 14, 2015 20:02
Show Gist options
  • Save chintanparikh/d1274988ef436a411eac to your computer and use it in GitHub Desktop.
Save chintanparikh/d1274988ef436a411eac to your computer and use it in GitHub Desktop.
/Users/chintanparikh/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/timeout.rb:38:in `throw': uncaught throw #<Timeout::ExitException: Timeout::ExitException> (ArgumentError)
from /Users/chintanparikh/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/timeout.rb:38:in `exception'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:203:in `send'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:203:in `block in send_packet'
from /Users/chintanparikh/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/timeout.rb:82:in `block in timeout'
from /Users/chintanparikh/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/timeout.rb:70:in `catch'
from /Users/chintanparikh/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/timeout.rb:70:in `timeout'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:202:in `send_packet'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:150:in `block in send'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:148:in `each'
from /Users/chintanparikh/Crap/project-3/rtp_socket.rb:148:in `send'
from client.rb:8:in `<main>'
require 'timeout'
require 'debugger'
class RTPSocket
def initialize ip_address, port, emu_ip, emu_port
@source_port = port
@source_ip = ip_address
@dest_port = nil
@dest_ip = nil
@emu_ip = emu_ip
@emu_port = emu_port
@connected = false
@terminated = false
@window = 1024
@timeout = 5
@rtt = 1
@mss = 1024
@buffer_size = @mss + 1024
@seq_num = 0
@send_seq_number = 0
@expected_seq_number = 0
@ack_number = 0
@udp_socket = UDPSocket.new
@udp_socket.bind(@source_ip, @source_port)
end
# Server socket will connect to an IP Address and Port if a SYN packet is received:
# Validate checksum. If SYN, extract sequence number, IP, port and checksum
# Randomly generate initial sequence number
# Send SYNACK back with ACK = sequence_num + 1. Also send servers initial seq num. Increment seq num
def connect ip, port
port = @source_port.to_i + 1
ip = @source_ip
@dest_ip = ip
@dest_port = port
@seq_num = Random.rand(1..(2**32))
synack = nil
while synack.nil?
send_syn_packet @seq_num
synack = receive_packet
end
@ack_num = synack.seq_num + 1
puts "Received SYNACK with seq_num #{synack.seq_num}, ack num #{synack.ack_num}"
send_ack_packet synack
puts "Sent final ACK packet. Connection established"
end
def disconnect
return unless @connected
if !@terminated
trm_packet = make_trm_packet
puts "Sending TRM packet"
send_packet trm_packet
puts "Received TRMACK"
@terminated = true
receive()
elsif @terminated # we have already terminated and just received a TRM packet from the other side
begin
packet = receive_packet
rescue
unless packet
send_ack_packet(packet)
receive()
end
end
end
@udp_socket.close
@connected = false
end
def listen
puts "Listening for a client to connect"
if @connected
puts "Already connected to a client"
return -1
end
step = 0
loop do
begin
packet = receive_packet
if step == 1 and packet.nil?
send_syn_ack_packet @seq_num, @ack_num
puts "Sent SYNACK packet"
end
if packet and packet.SYN.to_b and (step == 0 or step == 1)
puts "Received SYN Packet with sequence number #{packet.seq_num}"
@dest_ip = packet.dest_ip
@dest_port = packet.dest_port
@ack_num = packet.seq_num + 1
@seq_num = Random.rand(1..(2**32))
send_syn_ack_packet @seq_num, @ack_num
step = 1
puts "Sent SYNACK packet"
elsif packet and packet.ACK.to_b and (step == 1 or step == 2)
puts "Got final packet for listen step with ack_num #{packet.ack_num}"
break
elsif packet and packet.data and step == 2
break
end
rescue
end
end
puts "Connected to client"
@connected = true
end
def packetize data
packets = []
byte_pointer = 0
while byte_pointer + @mss < data.length
bytes_to_send = data[byte_pointer..byte_pointer + @mss]
packet = make_packet(bytes_to_send)
packets.push(packet)
byte_pointer += @mss
end
last_bytes = data[byte_pointer..-1]
packet = make_packet(last_bytes)
packets.push(packet)
packets
end
def send data
packets = packetize data
packets.each do |packet|
ack = nil
send_packet packet
end
packet = make_packet nil
send_packet packet
end
def receive
data_bytes = ''
disconnect = false
loop do
begin
packet = receive_packet
puts "Received packet - #{packet.data}"
rescue Timeout::Error
continue
end
if packet.TRM.to_b
disconnect = true
send_ack_packet packet
break
elsif packet.data == '' and not packet.ACK.to_b
puts "Received entire message"
send_ack_packet packet
break
elsif not packet.ACK.to_b
data_bytes += packet.data
# send_ack_packet packet
end
end
puts "Received entire message"
if disconnect
disconnect()
return nil
end
return data_bytes
end
def send_packet packet
puts "Sending packet with sequence number #{packet.seq_num}"
ack_packet = nil
packet_string = packet.to_s
@seq_num += packet_string.bytesize
while ack_packet == nil
begin
Timeout.timeout(@timeout) do
@udp_socket.send(packet_string, 0, @emu_ip, @emu_port)
puts "Sent packet"
recv_packet = receive_packet
puts "Received packet with ack number #{recv_packet.ack_num}" if recv_packet
if recv_packet and recv_packet.ACK and recv_packet.ack_num == packet.seq_num + 1
ack_packet = recv_packet
return ack_packet
end
end
rescue Timeout::Error
rescue Timeout::ExitException
end
end
end
def receive_packet
begin
Timeout.timeout(@timeout) do
packet_string, addr = @udp_socket.recvfrom(@buffer_size)
packet = Packet.from_string(packet_string)
puts "Returning packet"
return packet
# unless packet.corrupted?
# unless packet.is_duplicate?(@expected_seq_number)
# return packet
# else
# puts "Received duplicate packet"
# end
# else
# puts "Corrupted packets"
# end
end
rescue Timeout::Error
return nil
rescue Timeout::ExitException
return nil
end
end
def make_packet data
packet = Packet.new
packet.data = data
packet.source_ip = @source_ip
packet.source_port = @source_port
packet.dest_ip = @dest_ip
packet.dest_port = @dest_port
packet.seq_num = @seq_num
packet.SYN = false
packet.ACK = false
packet.TRM = false
packet.recalculate_checksum!
return packet
end
def make_syn_packet seq_num
packet = make_packet nil
packet.SYN = true
packet.seq_num = seq_num
packet.recalculate_checksum!
return packet
end
def make_trm_packet
packet = make_packet nil
packet.TRM = true
packet.recalculate_checksum!
end
def make_ack_packet packet_to_ack
packet = make_packet nil
packet.ACK = true
packet.ack_num = packet_to_ack.seq_num + 1
packet.recalculate_checksum!
return packet
end
def send_syn_packet seq_num
syn = make_syn_packet(seq_num)
packet_string = syn.to_s
puts "Sending SYN packet with seq num #{syn.seq_num}"
@seq_num = seq_num + syn.to_s.bytesize
@udp_socket.send(packet_string, 0, @emu_ip, @emu_port)
end
def send_ack_packet packet_to_ack
ack = make_ack_packet(packet_to_ack)
packet_string = ack.to_s
puts "Sending ACK packet with ack num #{ack.ack_num}"
@udp_socket.send(packet_string, 0, @emu_ip, @emu_port)
end
def send_syn_ack_packet seq_num, ack_num
packet = make_packet nil
packet.SYN = 1
packet.ACK = 1
packet.seq_num = seq_num
packet.ack_num = ack_num
packet.recalculate_checksum!
@seq_num = seq_num + packet.to_s.bytesize
puts "Sending SYNACK packet with seq num #{seq_num} ack num #{ack_num}"
@udp_socket.send(packet.to_s, 0, @emu_ip, @emu_port)
end
end
class Packet
HEADER_SIZE = 24
attr_accessor :source_ip, :dest_ip, :source_port, :dest_port, :seq_num, :ack_num, :SYN, :ACK, :TRM, :checksum, :data
def initialize
@source_ip = nil
@source_port = nil
@dest_ip = nil
@dest_port = nil
@seq_num = 0
@ack_num = 0
@SYN = false
@ACK = false
@TRM = false
@checksum = nil
@data = nil
end
def to_a
flags = 0
flags |= @SYN.to_i << 2
flags |= @ACK.to_i << 1
flags |= @TRM.to_i << 0
[@source_ip.split('.').map{|e| e.to_i}, @dest_ip.split('.').map{|e| e.to_i}, @source_port.to_i, @dest_port.to_i, @seq_num, @ack_num, flags, @checksum, @data].flatten
end
def to_s
to_a.pack('CCCCCCCCSSLLSSa*')
end
def self.from_string packet_string
pack = Packet.new
packet_array = packet_string.unpack('CCCCCCCCSSLLSSa*')
pack.source_ip = packet_array[0..3].join('.')
pack.dest_ip = packet_array[4..7].join('.')
pack.source_port = packet_array[8]
pack.dest_port = packet_array[9]
pack.seq_num = packet_array[10]
pack.ack_num = packet_array[11]
flags = packet_array[12]
pack.SYN = flags & 100
pack.ACK = flags & 10
pack.TRM = flags & 1
pack.checksum = packet_array[13]
pack.data = packet_array[14]
return pack
end
def recalculate_checksum!
@checksum = checksum(self)
end
# needs to return a 16 bit number
def checksum packet
return 1
end
def corrupted?
return false
end
def is_duplicate? num
return false
end
end
class TrueClass
def to_i
1
end
end
class FalseClass
def to_i
0
end
end
class Integer
def to_b
!self.zero?
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment