今回は、tracerouteと同じような機能を持つscanrouteというプログラムを作ってみた。引数で渡されたホストまでのルータを表示する。Googleなんか指定すると結果が返ってくるのに、Yahooを指定すると途中で止まってしまう。何でだろう?原因不明。『基礎からわかるTCP/IPネットワーク実験プログラム』に載ってるやつのRuby版なだけだったり。rootでないと動かない、またLinux以外で動くか不明。以下ソース
scanroute.rb
require 'socket' require './ip' require './udp' require './icmp' def make_ip_header(src_ip, dst_ip, iplen) ip = IPHeader.new ip.version = 4 ip.ip_tos = 0 ip.ip_hl = 5 ip.ip_id = 0 ip.ip_len = iplen ip.ip_off = 0 ip.ip_ttl = 64 ip.ip_p = Socket::IPPROTO_UDP ip.ip_src = src_ip ip.ip_dst = dst_ip ip.ip_sum = 0 return ip.to_byte_array() end def make_udp_header udp = UDPHeader.new udp.uh_sport = 33434 udp.uh_dport = 33434 udp.uh_ulen = 8 udp.uh_sum = 0 return udp.to_byte_array end if ARGV.size() < 1 puts 'usage ruby scanroute.rb dst_ip' exit(1) end send_sa = Socket.sockaddr_in(0, ARGV[0]) send_sd = Socket.open(Socket::AF_INET, Socket::SOCK_RAW, 255) recv_sd = Socket.open(Socket::AF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP) host = Socket.gethostbyname(ARGV[0])[3] ip_addr = sprintf("%d.%d.%d.%d", host[0], host[1], host[2], host[3]) packet = make_ip_header("0.0.0.0", ip_addr, 28) packet << make_udp_header() puts "scanroute #{ip_addr}" for ttl in 1..64 print ttl packet[8] = ttl & 0xFF i = 0 ip_header = nil icmp = nil for i in 0..3 if i == 3 break end break_flag = false send_sd.send(packet, 0, send_sa) while true buff, sockaddr = recv_sd.recvfrom(8192) ip_header = IPHeader.new(buff) buff.slice!(0..(ip_header.ip_hl << 2) - 1) icmp = ICMP.new(buff) if((icmp.icmp_type == ICMP::TIMXCEED && icmp.icmp_code == ICMP::TIMXCEED_INTRANS) || icmp.icmp_type == ICMP::UNREACH) break_flag = true break end end if break_flag break end end if(i < 3) puts " #{ip_header.ip_src}" if(icmp.icmp_type == ICMP::UNREACH) if(icmp.icmp_code == ICMP::UNREACH_PORT) puts "Reach!" elsif(icmp.icmp_code == ICMP::UNREACH_HOST) puts "Host Unreachable" elsif(icmp.icmp_code == ICMP::UNREACH_NET) puts "Network Unreachable" end break end end end send_sd.close() recv_sd.close()
ip.rb
class IPHeader attr_accessor :version attr_accessor :ip_hl attr_accessor :ip_tos attr_accessor :ip_len attr_accessor :ip_id attr_accessor :ip_off attr_accessor :ip_ttl attr_accessor :ip_p attr_accessor :ip_sum attr_accessor :ip_src attr_accessor :ip_dst def initialize(packet = nil) if(packet == nil) return end @version = (packet[0] >> 4) & 0x0F @ip_hl = packet[0] & 0x0F @ip_tos = packet[1] @ip_len = (packet[2] << 8) + packet[3] @ip_id = (packet[4] << 8) + packet[5] @ip_off = (packet[6] << 8) + packet[7] @ip_ttl = packet[8] @ip_p = packet[9] @ip_sum = (packet[10] << 8) + packet[11] @ip_src = ip_to_s(packet, 12) @ip_dst = ip_to_s(packet, 16) end def to_byte_array() array = " " array[0] = ((@version << 4) + @ip_hl) & 0xFF array[1] = @ip_tos & 0xFF array[2] = (@ip_len >> 8) & 0xFF array[3] = @ip_len & 0xFF array[4] = (@ip_id >> 8) & 0xFF array[5] = @ip_id & 0xFF array[6] = (@ip_off >> 8) & 0xFF array[7] = @ip_off & 0xFF array[8] = @ip_ttl & 0xFF array[9] = @ip_p & 0xFF array[10] = (@ip_sum >> 8) & 0xFF array[11] = @ip_sum & 0xFF s_to_ip(array, 12, @ip_src) s_to_ip(array, 16, @ip_dst) return array end def s_to_ip(array, index, str_ip) split = str_ip.split('.') for i in 0..3 array[index + i] = split[i].to_i() & 0xFF end end def ip_to_s(packet, index) return sprintf("%d.%d.%d.%d", packet[index], packet[index + 1], packet[index + 2], packet[index + 3]) end end
udp.rb
class UDPHeader attr_accessor :uh_sport attr_accessor :uh_dport attr_accessor :uh_ulen attr_accessor :uh_sum def initialize(packet = nil) if(packet == nil) return end @uh_sport = (packet[0] << 8) + packet[1] @uh_dport = (packet[2] << 8) + packet[3] @uh_ulen = (packet[4] << 8) + packet[5] @uh_sum = (packet[6] << 8) + packet[7] end def to_byte_array() array = " " array[0] = (@uh_sport >> 8) & 0xFF array[1] = @uh_sport & 0xFF array[2] = (@uh_dport >> 8) & 0xFF array[3] = @uh_dport & 0xFF array[4] = (@uh_ulen >> 8) & 0xFF array[5] = @uh_ulen & 0xFF array[6] = (@uh_sum >> 8) & 0xFF array[7] = @uh_sum & 0xFF return array end end
icmp.rb
class ICMP attr_reader :icmp_type attr_reader :icmp_code attr_reader :icmp_cksum ECHOREPLY = 0 UNREACH = 3 SOURCEQUENCH = 4 REDIRECT = 5 ECHO = 8 ROUTERADVERT = 9 ROUTERSOLICIT = 10 TIMXCEED = 11 PARAMPROB = 12 TSTAMP = 13 TSTAMPREPLY = 14 IREQ = 15 IREQREPLY = 16 MASKREQ = 17 MASKREPLY = 18 UNREACH_NET = 0 UNREACH_HOST = 1 UNREACH_PROTOCOL = 2 UNREACH_PORT = 3 UNREACH_NEEDFRAG = 4 UNREACH_SRCFAIL = 5 UNREACH_NET_UNKNOWN = 6 UNREACH_HOST_UNKNOWN = 7 UNREACH_ISOLATED = 8 UNREACH_NET_PROHIB = 9 UNREACH_HOST_PROHIB = 10 UNREACH_TOSNET = 11 UNREACH_TOSHOST = 12 UNREACH_FILTER_PROHIB = 13 UNREACH_HOST_PRECEDENCE = 14 REDIRECT_NET = 0 REDIRECT_HOST = 1 REDIRECT_TOSNET = 2 REDIRECT_TOSHOST = 3 TIMXCEED_INTRANS = 0 TIMXCEED_REASS = 1 PARAMPROB_ERRATPTR = 0 PARAMPROB_OPTABSENT = 1 PARAMPROB_LENGTH = 2 def initialize(packet) @icmp_type = packet[0] @icmp_code = packet[1] @icmp_cksum = (packet[2] << 8) + packet[3] end end