=begin Copyright (c) 2020 Marek Küthe This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. =end require "socket" def samcmd fircmd, seccmd, args = {} cmd = "#{fircmd} #{seccmd}" args.each_pair { |arg, value| cmd << " #{arg}=\"#{value.to_s}\"" } return cmd end def parsesamcmd ans ans.chomp! ans += " " f = ans.index " " s = ans.index " ", f + 1 w = ans[f+1...s] args = Hash.new loop do g1 = ans.index "=", s+1 break if g1 == nil g2 = nil if ans[g1+1] == "\"" g2 = ans.index "\"", g1+2 args[ans[s+1..g1-1]] = ans[g1+2..g2-1] else g2 = ans.index " ", g1+1 args[ans[s+1..g1-1]] = ans[g1+1..g2-1] end s = g2 end return [ans[0...f], w, args] end def ping dest pinger = TCPSocket.new "127.0.0.1", 7656 pinger.puts samcmd "HELLO", "VERSION" pinger.gets sta = Time.now pinger.puts samcmd "STREAM", "CONNECT", { "ID" => $samid, "DESTINATION" => dest } res = parsesamcmd pinger.gets fin = Time.now pinger.close return res, fin-sta end def randomcode len=5 symbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" res = "" rnd = Random.new for i in 0...len res += symbols[rnd.rand(0...symbols.length)] end return res end if ARGV.length == 0 puts <<~ENDOFUSAGE Usage: ruby #{__FILE__} [-v] [-l length] [-q quantity] [-c count] Options: -v\tverbose -l\tLength of the tunnels -q\tNumber of tunnels -c\tNumber of ping attempts ENDOFUSAGE exit end $eepsite = ARGV[0] $verbose = false $samnickname = "i2p-pinger" $samid = "i2p-pinger-#{randomcode}" $tunlen = 1 $tunq = 1 $tunb = 1 $count = 4 if ARGV.include? "-l" $tunlen = ARGV[ARGV.index("-l") + 1] end if ARGV.include? "-q" $tunq = ARGV[ARGV.index("-q") + 1] end if ARGV.include? "-c" $count = ARGV[ARGV.index("-c") + 1].to_i end if ARGV.include? "-b" $tunb = ARGV[ARGV.index("-b") + 1].to_i end if ARGV.include? "-v" $verbose = true end control = TCPSocket.new "127.0.0.1", 7656 control.puts samcmd "HELLO", "VERSION" res = parsesamcmd control.gets if res[2]["RESULT"] != "OK" $stderr.puts "Error [4] #{res.inspect}" exit end puts "--- Create sam session ---" if $verbose control.puts samcmd "SESSION", "CREATE", { "STYLE" => "STREAM", "ID" => $samid, "DESTINATION" => "TRANSIENT", "SIGNATURE_TYPE" => "ECDSA_SHA512_P521", "i2cp.leaseSetEncType" => "4,0", "i2cp.destination.sigType" => "EdDSA_SHA512_EdNAM25519", "inbound.length" => $tunlen.to_s, "outbound.length" => $tunlen.to_s, "inbound.nickname" => $samnickname, "outbound.nickname" => $samnickname, "outbound.priority" => "20", "inbound.priority" => "20", "inbound.quantity" => $tunq.to_s, "outbound.quantity" => $tunq.to_s, "outbound.backupQuantity" => $tunb.to_s, "inbound.backupQuantity" => $tunb.to_s } res = parsesamcmd control.gets if res[2]["RESULT"] != "OK" $stderr.puts "Error [1] #{res.inspect}" exit end $mydest = res[2]["DESTINATION"] puts "--- Naming lookup ---" if $verbose dest = nil if ARGV[0].include? ".i2p" control.puts samcmd "NAMING", "LOOKUP", { "NAME" => $eepsite } res = parsesamcmd control.gets if res[2]["RESULT"] != "OK" $stderr.puts "Error [2] #{res.inspect}" exit end dest = res[2]["VALUE"] else dest = ARGV[0] end controlping = Thread.new { res = parsesamcmd control.gets.chomp if res[0] == "PING" control.puts "PONG #{res[1]}" end } puts "--- Start pinging ---" if $verbose strhop = "hops" strhop = "hop" if $tunlen == 1 strtun = "tunnels" strtun = "tunnel" if $tunq == 1 print "The connection to the destination is established via #{$tunlen} #{strhop} with #{$tunq} #{strtun}.\n\n" times = Array.new status = Array.new # success = true; failed = false for i in 0...$count res, t = ping dest times << t if res[2]["RESULT"] == "OK" puts "Ping successful: #{t}s" status << true elsif res[2]["RESULT"] == "INVALID_KEY" $stderr.puts "The specified destination is not valid. No further ping attempts are made." break else puts "Ping failed - #{res[2].values.join " "}: #{t}s" status << false sleep 3 end sleep 1 end puts "\nPing evaluation: A total of " puts "\t#{$count} (100.0%) pings were sent." puts "\t#{status.count true} (#{(status.count(true).to_f / $count * 100).round 2}%) of them were successful and" puts "\t#{status.count false} (#{(status.count(false).to_f / $count * 100).round 2}%) failed." puts "\tThe average response time was #{times.inject(0.0) { |sum, x| sum + x} / times.length} seconds." controlping.exit control.puts "QUIT" control.close