=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 module SAMHelper def self.samcmd fircmd, seccmd = nil, args = {} cmd = "#{fircmd}#{seccmd ? " #{seccmd}" : ""}" args.each_pair { |arg, value| cmd << " #{arg}=\"#{value.to_s}\"" } return cmd end def self.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 end module I2PLookuper require "socket" def self.samlookup name, **args host = args.include?(:host) ? args[:host] : "127.0.0.1" port = args.include?(:port) ? args[:port] : 7656 puts "Connect to the SAM API ..." if $verbose begin sock = TCPSocket.new host, port rescue Exception => e return false, e end # hello version puts "Handshake with the SAM API is carried out ..." if $verbose sock.puts SAMHelper.samcmd "HELLO", "VERSION", { "MIN" => "1.0" } res = SAMHelper.parsesamcmd sock.gets if res[2]["RESULT"] != "OK" return false, res end # lookup request puts "Lookup the name ..." if $verbose sock.puts SAMHelper.samcmd "NAMING", "LOOKUP", { "NAME" => name } res = SAMHelper.parsesamcmd sock.gets if res[2]["RESULT"] != "OK" or res[2]["NAME"] != name return false, res end return true, res[2]["VALUE"] end def self.boblookup name, **args host = args.include?(:host) ? args[:host] : "127.0.0.1" port = args.include?(:port) ? args[:port] : 2827 puts "Connect to the BOB API ..." if $verbose begin sock = TCPSocket.new host, port rescue Exception => e return false, e end # welcome response puts "Handshake with the BOB API is carried out ..." if $verbose res = sock.gets.chomp unless res =~ /(BOB) (\d{2}.\d{2}.\d{2})/ return false, res end res = sock.gets.chomp if res != "OK" return false, res end # lookup request puts "Lookup the name ..." if $verbose sock.puts "lookup #{name}" res = sock.gets.chomp spl = res.split if spl[0] != "OK" return false, res end return true, spl[1] end def self.wclookup name, **args host = args.include?(:host) ? args[:host] : "127.0.0.1" port = args.include?(:port) ? args[:port] : 7657 uagent = args.include?(:uagent) ? args[:uagent] : "mi2pdig" puts "Connect to the web console and send the HTTP request ..." if $verbose begin sock = TCPSocket.new host, port rescue Exception => e return false, e end sock.puts "GET /susidns/details?h=#{name} HTTP/1.1" sock.puts "Host: 127.0.0.1" sock.puts "Connection: close" sock.puts "User-Agent: #{uagent}" sock.puts puts "Evaluate the received data ..." if $verbose res = sock.read.split "\n" resstat = res[0].chomp.split if resstat[1] != "200" || resstat[2] != "OK" return false, res end sock.close require "base64" begin l = res[59].scan(/\/)[0][0] rescue Exception => e return false, "The name could not be found or the data structure in the received code was changed." end if Base64.decode64(l) == "" return false, res[59] end return true, l end end # argument parsing if ARGV.length == 0 puts "No argument was given. If you need help you can call \"ruby #{__FILE__} --help\"." exit end name = nil args = {} $verbose = false $norout = true i = 0 while i < ARGV.length if ARGV[i].index "-" if ARGV[i] == "-s" or ARGV[i] == "--sam" args[:sam] = nil elsif ARGV[i] == "-b" or ARGV[i] == "--bob" args[:bob] = nil elsif ARGV[i] == "-c" or ARGV[i] == "--console" args[:wc] = nil elsif ARGV[i] == "-a" or ARGV[i] == "--auto" args[:auto] = nil elsif ARGV[i] == "-u" or ARGV[i] == "--uagent" args[:uagent] = nil elsif ARGV[i] == "-v" or ARGV[i] == "--verbose" $verbose = true elsif ARGV[i] == "-l" or ARGV[i] == "--lookup" $norout = false elsif ARGV[i] == "-p" or ARGV[i] == "--port" args[:port] = ARGV[i + 1] i += 1 elsif ARGV[i] == "-o" or ARGV[i] == "--host" args[:host] = ARGV[i + 1] i += 1 elsif ARGV[i] == "-h" or ARGV[i] == "--help" puts <<~EOH mi2pdig resolves an I2P name into a base64. It was written by Marek Küthe on 23/07/2020 in the programming language Ruby and published under the WTFPL license. Usage: -s, --sam Use the SAM API to resolve the name. -b, --bob Use the BOB API to resolve the name. -c, --console Accesses the I2P web console to resolve a name. -a, --auto Try out all three options offered to resolve the name. First the SAM API, then the BOB API and then the Web Console. -p, --port Specifies the port for the the possibility. If you choose automatic, all three options will be tried on this port. -s, --host Specifies the host for the the possibility. If you choose automatic, all three options will be tried on this host. -l, --lookup If the resolution is successful, only outputs the base64. -u, --uagent Specifies the HTTP user agent in the web console option. If none is specified, "mi2pdig" is used. -h, --help Displays this help page. -v, --verbose Activates verbose output Example: ruby #{__FILE__} -s -o 127.0.0.1 -p 7656 stats.i2p ruby #{__FILE__} --sam --host 127.0.0.1 --port 7656 stats.i2p ruby #{__FILE__} stats.i2p -s -o 127.0.0.1 -p 7656 EOH exit end else if name == nil name = ARGV[i] else $stderr << "Error: Only one name may be specified, which should be resolved." exit end end i += 1 end if args.has_key? :sam res = I2PLookuper.samlookup name, **args if res[0] == true puts "The name was successfully resolved using the SAM API." if $norout puts res[1] else $stderr << "Error: #{res.inspect}" end elsif args.has_key? :bob res = I2PLookuper.boblookup name, **args if res[0] == true puts "The name was successfully resolved using the BOB API." if $norout puts res[1] else $stderr << "Error: #{res.inspect}" end elsif args.has_key? :wc res = I2PLookuper.wclookup name, **args if res[0] == true puts "The name was successfully resolved using the I2P Web Console." if $norout puts res[1] else $stderr << "Error: #{res.inspect}" end else res1 = I2PLookuper.samlookup name, **args if res1[0] == true puts "The name was successfully resolved using the SAM API." if $norout puts res1[1] exit end res2 = I2PLookuper.boblookup name, **args if res2[0] == true puts "The name was successfully resolved using the BOB API." if $norout puts res2[1] exit end res3 = I2PLookuper.wclookup name, **args if res3[0] == true puts "The name was successfully resolved using the I2P Web Console." if $norout puts res3[1] exit end puts "The name could not be lookeped." puts "#{res1[1].inspect}" puts "#{res2[1].inspect}" puts "#{res3[1].inspect}" end