Reverse DNS in Ruby? - ruby

I'm in an environment with a lot of computers that haven't been
properly inventoried. Basically, no one knows which IP goes with which
mac address and which hostname. So I wrote the following:
# This script goes down the entire IP range and attempts to
# retrieve the Hostname and mac address and outputs them
# into a file. Yay!
require "socket"
TwoOctets = "10.26"
def computer_exists?(computerip)
system("ping -c 1 -W 1 #{computerip}")
end
def append_to_file(line)
file = File.open("output.txt", "a")
file.puts(line)
file.close
end
def getInfo(current_ip)
begin
if computer_exists?(current_ip)
arp_output = `arp -v #{current_ip}`
mac_addr = arp_output.to_s.match(/..:..:..:..:..:../)
host_name = Socket.gethostbyname(current_ip)
append_to_file("#{host_name[0]} - #{current_ip} - #{mac_addr}\n")
end
rescue SocketError => mySocketError
append_to_file("unknown - #{current_ip} - #{mac_addr}")
end
end
(6..8).each do |i|
case i
when 6
for j in (1..190)
current_ip = "#{TwoOctets}.#{i}.#{j}"
getInfo(current_ip)
end
when 7
for j in (1..255)
current_ip = "#{TwoOctets}.#{i}.#{j}"
getInfo(current_ip)
end
when 8
for j in (1..52)
current_ip = "#{TwoOctets}.#{i}.#{j}"
getInfo(current_ip)
end
end
end
Everything works except it does not find a Reverse DNS.
Sample output that I'm getting is this:
10.26.6.12 - 10.26.6.12 - 00:11:11:9B:13:9F
10.26.6.17 - 10.26.6.17 - 08:00:69:9A:97:C3
10.26.6.18 - 10.26.6.18 - 08:00:69:93:2C:E2
If I do nslookup 10.26.6.12 then I get the correct reverse DNS so
that shows that my machine is seeing the DNS server.
I have tried Socket.gethostbyname, gethostbyaddr, but it doesn't work.
Any guidance will be much appreciated.

Today I also needed reverse DNS lookup and I've found very simple standard solution:
require 'resolv'
host_name = Resolv.getname(ip_address_here)
It seems it uses timeout which helps in rough cases.

I would check out getaddrinfo. If you replace the line:
host_name = Socket.gethostbyname(current_ip)
with:
host_name = Socket.getaddrinfo(current_ip, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)[0][1]
The getaddrinfo function returns an array of arrays. You can read more about it at:
Ruby Socket Docs

This also works:
host_name = Socket.getaddrinfo(current_ip,nil)
append_to_file("#{host_name[0][2]} - #{current_ip} - #{mac_addr}\n")
I'm not sure why gethostbyaddr didn't also work.

Related

How to ping a set of IPs stored in a text file and validate where the IP is reachable or not in Ruby?

I am very new to Ruby so any help will be very useful. :)
What I aim to do with my program is:
Open a txt file with a list of IPs stored in it
Fetch each IP one by one and ping it
If the IP is reachable, return a TRUE value
If IP is unreachable, return a FALSE value
All non-reachable IPs to be written into another txt file
Below is the program I pieced together from different online help sources:
require 'timeout'
require 'socket'
#PING LOGIC
class Ping
def self.pingecho(host, timeout=5, service="echo")
puts host
begin
while(timeout) do
s = TCPSocket.new(host, service)
s.close
end
rescue Errno::ECONNREFUSED
return true
rescue Timeout::Error, StandardError
return false
end
return true
end
end
#opening the file with list of IPs
File.open('Ips.xml', 'r'). each do |line|
hostip = line
#passing each line to the class for the ping test
if (p Ping.pingecho(hostip) == 'true')
return
else
#writing all non-reachable IPs to another file
File.open('Not reachable.txt','a') do |linea|
linea.puts hostip + "\n"
end
end
end
This does not give me any error when I execute it, but provides a FALSE result for all IPs in the txt file which I know are reachable.
I know that separately all the components of this program work fine: The ping logic works fine if I just pass an IP directly to it (For example: p Ping.pingecho("10.40.220.34")).
The file opening; reading; and writing into other file also work fine as I tested them separately.
The issue seems to be with the manner in which the IP is passed to the class from the txt file.
if (p Ping.pingecho(hostip) == 'true')
Ping::pingecho returns either true or false, but it never returns 'true', therefore this condition will always be false, and the conditional expression will always evaluate the else branch.

Ruby IO from a service at port 6557 in Sinatra

I have to take a dump of a service in sinatra and display it in the content area of the webpage.
The Service I have to access via code runs on server at port 6557. It doesnt use any encryption or authentication. Its a plain readonly request response thingy like http.
Here is what works in teminal
$ echo "GET hosts" | nc 192.168.1.1 6557
gives me the intended output. I need to do something similar using the sinatra application.
I wrote this code but is grossly incorrect. Can sombody help me with code or lookup materials or examples.
get '/' do
host = "192.168.1.1"
port = 6557
dat = ""
#socket = TCPSocket.open (host, port)
while(true)
if(IO.select([],[],[#socket],0))
socket.close
return
end
begin
while( (data = #socket.recv_nonblock(100)) != "")
dat = dat+ data
end
rescue Errno::EAGAIN
end
begin
#str = "GET hosts"
#socket.puts(#str);
rescue Errno::EAGAIN
rescue EOFError
exit
end
IO.select([#socket], [#socket], [#socket])
end
#line = dat
erb :info
end
The code on execution just hangs up.
Also if possible please give some links to read up to get a conceptual context of the problem.
I think the Ruby equivalent to your shell command should be as simple as:
require "socket"
socket = TCPSocket.new "192.168.1.1", 6557
socket.puts "GET hosts"
socket.read
According to the docs, #read should close the socket automatically, so you don't need to worry about doing that manually.
You can execute shell commands directly from ruby using backticks or the system command. Something like this may work for you:
get "/" do
#line = `echo "GET hosts" | nc 192.168.1.1 6557`
erb :info
end
Check out the ruby docs for Kernel#system for more info.

How to get IP of Heroku server, where is placed my app?

I am trying to get IP address of server, where is running my app. In PHP exists the function called GetHostByName() (or something like that), what is the alternative for Ruby?
You can use this (no shell required):
require 'socket'
def local_ip
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
UDPSocket.open do |s|
s.connect '64.233.187.99', 1
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
Output:
# irb:0> local_ip
# => "192.168.0.1"
I use this function, but credit goes to: http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/

How can I get the list of computer names in Windows Network using Ruby

I know that the Ruby have a lot of gems allowing programmers write a really beatiful code.
This short example by default scans all addresses on subnet 192.168.1.x.
require 'ipscanner'
a = IPScanner.scan
#=> ["192.168.1.1", "192.168.1.4"]
How can I get computer names instead of ip's? Is there any gem that implements Window API (NetBIOS)?
Maybe it's better to use smth. like macaddr http://rubygems.org/gems/macaddr that parse 'ipconfig /all' (in my case - 'nbtstat')
That ipscanner gem looks like it just cycles through 192.168.1.(1..254) and builds an array of the ip addresses that didn't timeout this line: TCPSocket.new(ip_address)
So one method might be to reverse lookup the host name given an IP address. Looks like the Socket class can help you out.
For instance:
Socket.do_not_reverse_lookup = false
s = Socket.getaddrinfo(’66.249.67.49′,nil)
hostname = s[0][4]
or
host = Resolv.new.getname('66.249.67.49')
Code taken from a blog.
I don't have any computers on the network to test this, and I don't know enough about this stuff to know if there's a difference between a computer's name at an IP and the host name at an IP. If the above doesn't work, it can't be too far off.
Thanks to #danneu.
I wrote simple example, works great on Windows 7.
Check your firewall policy (you can get just ip instead of computer name)! Name of the computer is the third parameter so use hostname = Socket.getaddrinfo('192.168.1.10', nil)[0][2].
require 'ipscanner'
Socket.do_not_reverse_lookup = false
IPScanner.scan.each {
|i|
puts "#{Socket.getaddrinfo(i, nil)}"
}
Output:
[["AF_INET", 0, "STARBOY", 192.168.1.2, 2, 0, 0]]
[["AF_INET", 0, 192.168.1.1, 192.168.1.1, 2, 0, 0]]
[["AF_INET", 0, "mcx-PC", 192.168.1.4, 2, 0, 0]]
This example is more illustrative and it works consistently waiting for the completion of all threads. Pingecho timeout increased to 50 ms.
require 'ipscanner'
puts 'List of computers in your LAN. Please wait ...'
class IPScanner
# override scan method
def self.scan(ip_base = '192.168.1.', range = 1..254, t = 50)
computers = []
threads = []
Socket.do_not_reverse_lookup = false
(range).map {
|i|
threads << Thread.new {
ip = ip_base + i.to_s
if pingecho(ip, t)
computers << Socket.getaddrinfo(ip, nil)[0][2]
end
}
}.join
# wait for all threads to terminate
threads.each { |thread| thread.join }
return computers
end
end
puts "#{IPScanner.scan}"
Output:
["STARBOY", "192.168.1.1", "mcx-PC"]

Ruby: get local IP (nix)

I need to get my IP (that is DHCP). I use this in my environment.rb:
LOCAL_IP = `ifconfig wlan0`.match(/inet addr:(\d*\.\d*\.\d*\.\d*)/)[1] || "localhost"
But is there rubyway or more clean solution?
A server typically has more than one interface, at least one private and one public.
Since all the answers here deal with this simple scenario, a cleaner way is to ask Socket for the current ip_address_list() as in:
require 'socket'
def my_first_private_ipv4
Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
end
def my_first_public_ipv4
Socket.ip_address_list.detect{|intf| intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast? and !intf.ipv4_private?}
end
Both returns an Addrinfo object, so if you need a string you can use the ip_address() method, as in:
ip= my_first_public_ipv4.ip_address unless my_first_public_ipv4.nil?
You can easily work out the more suitable solution to your case changing Addrinfo methods used to filter the required interface address.
require 'socket'
def local_ip
orig = Socket.do_not_reverse_lookup
Socket.do_not_reverse_lookup =true # turn off reverse DNS resolution temporarily
UDPSocket.open do |s|
s.connect '64.233.187.99', 1 #google
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
puts local_ip
Found here.
Here is a small modification of steenslag's solution
require "socket"
local_ip = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}

Resources