-
-
Save chintanparikh/b802e6f0e2f5b115de99 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative '../rtp_socket' | |
require 'socket' | |
socket = RTPSocket.new '0.0.0.0', ARGF.argv[0], ARGF.argv[1], ARGF.argv[2] | |
# @socket = RTPSocket.new '0.0.0.0', '5678', '0.0.0.0', '5000' | |
def parse command | |
case command | |
when "connect" | |
@socket.connect ARGF.argv[1], ARGF.argv[2] | |
when /^get/ | |
filename = command.split(' ')[1] | |
@socket.send("GET #{filename}") | |
response = @socket.receive | |
File.open(filename, 'w') {|f| f.write(response)} | |
when /^post/ | |
when /^window/ | |
when 'disconnect' | |
begin | |
@socket.client_disconnect | |
rescue IOError | |
end | |
end | |
end | |
loop do | |
print "> " | |
command = gets.chomp | |
parse command | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
client.rb:10:in `parse': undefined method `connect' for nil:NilClass (NoMethodError) | |
from client.rb:30:in `block in <main>' | |
from client.rb:27:in `loop' | |
from client.rb:27:in `<main>' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = 1 | |
@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)) | |
# Send SYN | |
# Receive SYNACK | |
# Send ACK | |
# Wait for timeout | |
states = ['INITIAL', 'SENT_SYN', 'GOT_SYNACK', 'SENT_ACK'] | |
state = 'INITIAL' | |
synack = nil | |
loop do | |
case state | |
when 'INITIAL' | |
puts "Sending SYN packet" | |
send_syn_packet @seq_num | |
state = 'SENT_SYN' | |
when 'SENT_SYN' | |
packet = receive_packet | |
if !packet.nil? and packet.SYN.to_b and packet.ACK.to_b | |
@ack_num = packet.seq_num + 1 | |
synack = packet | |
puts "Got SYNACK packet" | |
state = 'GOT_SYNACK' | |
else | |
state = 'INITIAL' | |
end | |
when 'GOT_SYNACK' | |
puts "Sending ACK packet" | |
send_ack_packet synack | |
state = "SENT_ACK" | |
when 'SENT_ACK' | |
packet = receive_packet(@timeout*3) | |
if packet and packet.SYN.to_b and packet.ACK.to_b | |
puts "Got SYNACK packet, not connected yet" | |
state = 'GOT_SYNACK' | |
else | |
@connected = true | |
puts 'Connection established' | |
break | |
end | |
end | |
end | |
end | |
def client_disconnect | |
# States | |
# Sent FIN, waiting for FINACK | |
# Got FINACK, waiting for FIN | |
# Got FIN, sending FINACK | |
# Sent FINACK, waiting to see if we get another FIN until @timeout, otherwise disconenct | |
# If we get another FIN decrement state | |
states = ['INITIAL', 'SENT_FIN', 'GOT_FINACK', 'GOT_FIN', 'WAITING_FOR_TIMEOUT'] | |
unless @connected | |
puts "Already disconnected" | |
return | |
end | |
state = states[0] | |
loop do | |
case state | |
when 'INITIAL' | |
fin_packet = make_fin_packet | |
puts "Sending FIN packet" | |
send_special_packet fin_packet | |
state = states[1] | |
when 'SENT_FIN' | |
packet = receive_packet | |
if packet and packet.FIN.to_b and packet.ACK.to_b | |
puts "Got FINACK packet" | |
state = states[2] | |
else | |
state = states[0] | |
end | |
when 'GOT_FINACK' | |
packet = receive_packet | |
if packet and packet.FIN.to_b | |
puts "Got FIN packet" | |
state = states[3] | |
else | |
state = states[1] | |
end | |
when 'GOT_FIN' | |
puts "Sending FINACK packet" | |
finack_packet = make_and_send_special_packet do |packet| | |
packet.FIN = true | |
packet.ACK = true | |
end | |
state = states[4] | |
when 'WAITING_FOR_TIMEOUT' | |
packet = receive_packet @timeout * 3 | |
if packet and packet.FIN.to_b | |
puts "Got FIN, not disconnecting" | |
state = states[2] | |
elsif packet and packet.FIN.to_b and packet.ACK.to_b | |
puts "Got FINACK, not disconnecting" | |
state = states[3] | |
else | |
puts "No packet received, disconnecting" | |
@udp_socket.close | |
@connected = false | |
break | |
end | |
end | |
end | |
end | |
def server_disconnect | |
states = ['INITIAL', 'GOT_FIN', 'SENT_FINACK', 'SENT_FIN', 'GOT_FINACK'] | |
state = states[1] | |
loop do | |
case state | |
when 'INITIAL' | |
packet = receive_packet | |
if packet and packet.FIN.to_b | |
puts "Got FIN packet" | |
state = 'GOT_FIN' | |
end | |
when 'GOT_FIN' | |
puts "Sending FINACK packet" | |
finack_packet = make_and_send_special_packet do |packet| | |
packet.FIN = true | |
packet.ACK = true | |
end | |
state = 'SENT_FINACK' | |
when 'SENT_FINACK' | |
fin_packet = make_fin_packet | |
puts "Sending FIN packet" | |
send_special_packet fin_packet | |
state = 'SENT_FIN' | |
when 'SENT_FIN' | |
packet = receive_packet | |
if packet and packet.FIN.to_b and packet.ACK.to_b | |
puts "Got FINACK packet" | |
state = 'GOT_FINACK' | |
else | |
state = 'GOT_FIN' | |
end | |
when 'GOT_FINACK' | |
packet = receive_packet @timeout * 3 | |
if packet and packet.FIN.to_b and packet.ACK.to_b | |
state = 'GOT_FINACK' | |
puts "Got FINACK, not disconnecting" | |
elsif packet and packet.FIN.to_b | |
puts "Got FIN, not disconnecting" | |
state = 'GOT_FIN' | |
else | |
puts "No packet received, disconnecting" | |
@udp_socket.close | |
@connected = false | |
break | |
end | |
end | |
end | |
# Wait for FIN | |
# Send FINACK | |
# Send FIN | |
# Recieve FINACK | |
end | |
def listen | |
puts "Listening for a client to connect" | |
if @connected | |
puts "Already connected to a client" | |
return -1 | |
end | |
# Get SYN | |
# Send SYNACK | |
# Get ACK | |
states = ['INITIAL', 'GOT_SYN', 'SENT_SYNACK', 'GOT_ACK'] | |
state = 'INITIAL' | |
loop do | |
case state | |
when 'INITIAL' | |
packet = receive_packet | |
if packet and packet.SYN.to_b | |
puts 'Got SYN packet' | |
@dest_ip = packet.dest_ip | |
@dest_port = packet.dest_port | |
@ack_num = packet.seq_num + 1 | |
@seq_num = Random.rand(1..(2**32)) | |
state = 'GOT_SYN' | |
end | |
when 'GOT_SYN' | |
puts "Sending SYNACK packet" | |
send_syn_ack_packet @seq_num, @ack_num | |
state = 'SENT_SYNACK' | |
when 'SENT_SYNACK' | |
packet = receive_packet | |
if packet and packet.ACK.to_b | |
puts 'Got ACK packet' | |
state = 'GOT_ACK' | |
elsif packet and packet.SYN.to_b | |
state = 'GOT_SYN' | |
puts 'Got SYN packet' | |
else | |
state = "GOT_SYN" | |
end | |
when 'GOT_ACK' | |
packet = receive_packet @timeout*3 | |
if packet and packet.SYN.to_b | |
state = 'GOT_SYN' | |
puts "Got SYN packet, not connected yet" | |
else | |
puts "Connected to client" | |
@connected = true | |
break | |
end | |
end | |
end | |
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, @timeout * 3 | |
end | |
packet = make_packet nil | |
send_packet packet, @timeout * 3 | |
end | |
def receive | |
data_bytes = '' | |
disconnect = false | |
length = 0 | |
loop do | |
begin | |
packet = receive_packet | |
puts "Received packet - #{packet.data}" if packet | |
rescue Timeout::Error | |
continue | |
end | |
if packet and packet.FIN.to_b | |
disconnect = true | |
break | |
elsif packet and packet.data == '' and not packet.ACK.to_b | |
puts "Received entire message" | |
send_ack_packet packet | |
fallback = receive_packet | |
if fallback.nil? | |
break | |
else | |
send_ack_packet packet | |
end | |
elsif packet and not packet.ACK.to_b | |
length = packet.length | |
data_bytes += packet.data | |
send_ack_packet packet | |
end | |
end | |
puts "Received entire message" | |
if disconnect | |
server_disconnect | |
return nil | |
end | |
return data_bytes[0..length-1] | |
end | |
def send_packet packet, timeout = @timeout | |
puts "Sending packet #{packet.data} 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 with data #{packet.data}" | |
recv_packet = receive_packet | |
puts "Received packet with ack number #{recv_packet.ack_num}" if recv_packet | |
# debugger if recv_packet | |
if recv_packet and recv_packet.ACK.to_b | |
ack_packet = recv_packet | |
return ack_packet | |
end | |
end | |
rescue Timeout::Error | |
rescue Timeout::ExitException | |
rescue ArgumentError | |
end | |
end | |
end | |
def receive_packet timeout = @timeout | |
begin | |
Timeout.timeout(timeout) do | |
packet_string, addr = @udp_socket.recvfrom(@buffer_size) | |
packet = Packet.from_string(packet_string) | |
puts "Received packet with:" | |
puts "\t seq_num: #{packet.seq_num}" | |
puts "\t ack_num: #{packet.ack_num}" | |
puts "\t #{"SYN, " if packet.SYN.to_b}#{"ACK, " if packet.ACK.to_b}#{"FIN" if packet.FIN.to_b}" | |
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.FIN = 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_fin_packet | |
packet = make_packet nil | |
packet.FIN = true | |
packet.recalculate_checksum! | |
return packet | |
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_special_packet packet | |
puts "Sending packet with:" | |
puts "\t seq_num: #{packet.seq_num}" | |
puts "\t ack_num: #{packet.ack_num}" | |
puts "\t #{"SYN, " if packet.SYN.to_b}#{"ACK, " if packet.ACK.to_b}#{"FIN" if packet.FIN.to_b}" | |
@udp_socket.send(packet.to_s, 0, @emu_ip, @emu_port) | |
end | |
def make_and_send_special_packet | |
packet = make_packet nil | |
yield packet | |
packet.recalculate_checksum! | |
puts "Sending packet with:" | |
puts "\t seq_num: #{packet.seq_num}" | |
puts "\t ack_num: #{packet.ack_num}" | |
puts "\t #{"SYN" if packet.SYN.to_b}#{"ACK" if packet.ACK.to_b}#{"FIN" if packet.FIN.to_b}" | |
@udp_socket.send(packet.to_s, 0, @emu_ip, @emu_port) | |
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 = 26 | |
attr_accessor :source_ip, :dest_ip, :source_port, :dest_port, :seq_num, :ack_num, :SYN, :ACK, :FIN, :checksum, :length, :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 | |
@FIN = false | |
@checksum = nil | |
@length = 0 | |
@data = nil | |
end | |
def to_a | |
flags = 0 | |
flags |= @SYN.to_i << 2 | |
flags |= @ACK.to_i << 1 | |
flags |= @FIN.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, @length, @data].flatten | |
end | |
def to_s | |
to_a.pack('CCCCCCCCSSLLSSCa*') | |
end | |
def self.from_string packet_string | |
pack = Packet.new | |
packet_array = packet_string.unpack('CCCCCCCCSSLLSSCa*') | |
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.FIN = flags & 1 | |
pack.checksum = packet_array[13] | |
pack.length = packet_array[14] | |
pack.data = packet_array[15] | |
return pack | |
end | |
def recalculate_checksum! | |
@checksum = checksum(self) | |
@length = @data.length unless @data.nil? | |
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 | |
def to_b | |
self | |
end | |
end | |
class FalseClass | |
def to_i | |
0 | |
end | |
def to_b | |
self | |
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