diff --git a/lib/ping.rb b/lib/ping.rb new file mode 100644 index 0000000..c97e2a6 --- /dev/null +++ b/lib/ping.rb @@ -0,0 +1,70 @@ +require "socket" + +ICMPHeader = Data.define(:type, :code, :checksum, :_ping_id, :_sequence_id, :data) + +ICMP_ECHOREPLY = 0 # Echo reply +ICMP_ECHO = 8 # Echo request +ICMP_SUBCODE = 0 + +# Perform a checksum on the message. This is the sum of all the short +# words and it folds the high order bits into the low order bits. +# +def checksum(msg) + length = msg.length + num_short = length / 2 + check = 0 + + msg.unpack("n#{num_short}").each do |short| + check += short + end + + if (length % 2).positive? + check += msg[length-1, 1].unpack1("C") << 8 + end + + check = (check >> 16) + (check & 0xffff) + ~((check >> 16) + check) & 0xffff +end + +ip_address = "127.0.0.1" # "example.com" # "timecrafters.org" # +@ping_id = 92_459_064_892 & 0xffff +@sequence = 1 % 65_536 +data = "" +data_size = 56 +data_size.times { |n| data << (n % 256).chr } + +check = 0 +packer = "C2 n3 A" << data_size.to_s +message = [ICMP_ECHO, ICMP_SUBCODE, check, @ping_id, @sequence, data].pack(packer) +check = checksum(message) +message = [ICMP_ECHO, ICMP_SUBCODE, check, @ping_id, @sequence, data].pack(packer) +message_header = ICMPHeader.new(*[ICMP_ECHO, ICMP_SUBCODE, check, @ping_id, @sequence, data]) +socket = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, Socket::IPPROTO_ICMP) +socket.send(message, 0, Socket.pack_sockaddr_in(0, ip_address)) +s = Time.now +loop do + data, _addrinfo = socket.recvfrom(1500) + pp [message, data] + header = ICMPHeader.new(*data.unpack("C2 n3 A*")) + pp [message_header, header] + # reply_type = data[20, 2].unpack1("C2") + # pp reply_type + + ping_id = header._ping_id + sequence = header._sequence_id + + case header.type + when ICMP_ECHOREPLY + puts "ECHOREPLY" + end + + pp [@ping_id, ping_id] + pp [@sequence, sequence] + + if ping_id == @ping_id && sequence == @sequence && reply_type == ICMP_ECHOREPLY + puts "PING OKAY: #{Time.now - s}s" + break + end + + break +end