Ruby: get local IP (nix) - ruby

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}

Related

Increasing Ruby Resolv Speed

Im trying to build a sub-domain brute forcer for use with my clients - I work in security/pen testing.
Currently, I am able to get Resolv to look up around 70 hosts in 10 seconds, give or take and wanted to know if there was a way to get it to do more. I have seen alternative scripts out there, mainly Python based that can achieve far greater speeds than this. I don't know how to increase the number of requests Resolv makes in parallel, or if i should split the list up. Please note I have put Google's DNS servers in the sample code, but will be using internal ones for live usage.
My rough code for debugging this issue is:
require 'resolv'
def subdomains
puts "Subdomain enumeration beginning at #{Time.now.strftime("%H:%M:%S")}"
subs = []
domains = File.open("domains.txt", "r") #list of domain names line by line.
Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
File.open("tiny.txt", "r").each_line do |subdomain|
subdomain.chomp!
domains.each do |d|
puts "Checking #{subdomain}.#{d}"
ip = Resolv.new.getaddress "#{subdomain}.#{d}" rescue ""
if ip != nil
subs << subdomain+"."+d << ip
end
end
end
test = subs.each_slice(4).to_a
test.each do |z|
if !z[1].nil? and !z[3].nil?
puts z[0] + "\t" + z[1] + "\t\t" + z[2] + "\t" + z[3]
end
end
puts "Finished at #{Time.now.strftime("%H:%M:%S")}"
end
subdomains
domains.txt is my list of client domain names, for example google.com, bbc.co.uk, apple.com and 'tiny.txt' is a list of potential subdomain names, for example ftp, www, dev, files, upload. Resolv will then lookup files.bbc.co.uk for example and let me know if it exists.
One thing is you are creating a new Resolv instance with the Google nameservers, but never using it; you create a brand new Resolv instance to do the getaddress call, so that instance is probably using some default nameservers and not the Google ones. You could change the code to something like this:
resolv = Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
# ...
ip = resolv.getaddress "#{subdomain}.#{d}" rescue ""
In addition, I suggest using the File.readlines method to simplify your code:
domains = File.readlines("domains.txt").map(&:chomp)
subdomains = File.readlines("tiny.txt").map(&:chomp)
Also, you're rescuing the bad ip and setting it to the empty string, but then in the next line you test for not nil, so all results should pass, and I don't think that's what you want.
I've refactored your code, but not tested it. Here is what I came up with, and may be clearer:
def subdomains
puts "Subdomain enumeration beginning at #{Time.now.strftime("%H:%M:%S")}"
domains = File.readlines("domains.txt").map(&:chomp)
subdomains = File.readlines("tiny.txt").map(&:chomp)
resolv = Resolv.new(:nameserver => ['8.8.8.8', '8.8.4.4'])
valid_subdomains = subdomains.each_with_object([]) do |subdomain, valid_subdomains|
domains.each do |domain|
combined_name = "#{subdomain}.#{domain}"
puts "Checking #{combined_name}"
ip = resolv.getaddress(combined_name) rescue nil
valid_subdomains << "#{combined_name}#{ip}" if ip
end
end
valid_subdomains.each_slice(4).each do |z|
if z[1] && z[3]
puts "#{z[0]}\t#{z[1]}\t\t#{z[2]}\t#{z[3]}"
end
end
puts "Finished at #{Time.now.strftime("%H:%M:%S")}"
end
Also, you might want to check out the dnsruby gem (https://github.com/alexdalitz/dnsruby). It might do what you want to do better than Resolv.
[Note: I've rewritten the code so that it fetches the IP addresses in chunks. Please see https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed. You can modify the RESOLVE_CHUNK_SIZE constant to balance performance with resource load.]
I've rewritten this code using the dnsruby gem (written mainly by Alex Dalitz in the UK, and contributed to by myself and others). This version uses asynchronous message processing so that all requests are being processed pretty much simultaneously. I've posted a gist at https://gist.github.com/keithrbennett/3cf0be2a1100a46314f662aea9b368ed but will also post the code here.
Note that since you are new to Ruby, there are lots of things in the code that might be instructive to you, such as method organization, use of Enumerable methods (e.g. the amazing 'partition' method), the Struct class, rescuing a specific Exception class, %w, and Benchmark.
NOTE: LOOKS LIKE STACK OVERFLOW ENFORCES A MAXIMUM MESSAGE SIZE, SO THIS CODE IS TRUNCATED. GO TO THE GIST IN THE LINK ABOVE FOR THE COMPLETE CODE.
#!/usr/bin/env ruby
# Takes a list of subdomain prefixes (e.g. %w(ftp xyz)) and a list of domains (e.g. %w(nytimes.com afp.com)),
# creates the subdomains combining them, fetches their IP addresses (or nil if not found).
require 'dnsruby'
require 'awesome_print'
RESOLVER = Dnsruby::Resolver.new(:nameserver => %w(8.8.8.8 8.8.4.4))
# Experiment with this to get fast throughput but not overload the dnsruby async mechanism:
RESOLVE_CHUNK_SIZE = 50
IpEntry = Struct.new(:name, :ip) do
def to_s
"#{name}: #{ip ? ip : '(nil)'}"
end
end
def assemble_subdomains(subdomain_prefixes, domains)
domains.each_with_object([]) do |domain, subdomains|
subdomain_prefixes.each do |prefix|
subdomains << "#{prefix}.#{domain}"
end
end
end
def create_query_message(name)
Dnsruby::Message.new(name, 'A')
end
def parse_response_for_address(response)
begin
a_answer = response.answer.detect { |a| a.type == 'A' }
a_answer ? a_answer.rdata.to_s : nil
rescue Dnsruby::NXDomain
return nil
end
end
def get_ip_entries(names)
queue = Queue.new
names.each do |name|
query_message = create_query_message(name)
RESOLVER.send_async(query_message, queue, name)
end
# Note: although map is used here, the record in the output array will not necessarily correspond
# to the record in the input array, since the order of the messages returned is not guaranteed.
# This is indicated by the lack of block variable specified (normally w/map you would use the element).
# That should not matter to us though.
names.map do
_id, result, error = queue.pop
name = _id
case error
when Dnsruby::NXDomain
IpEntry.new(name, nil)
when NilClass
ip = parse_response_for_address(result)
IpEntry.new(name, ip)
else
raise error
end
end
end
def main
# domains = File.readlines("domains.txt").map(&:chomp)
domains = %w(nytimes.com afp.com cnn.com bbc.com)
# subdomain_prefixes = File.readlines("subdomain_prefixes.txt").map(&:chomp)
subdomain_prefixes = %w(www xyz)
subdomains = assemble_subdomains(subdomain_prefixes, domains)
start_time = Time.now
ip_entries = subdomains.each_slice(RESOLVE_CHUNK_SIZE).each_with_object([]) do |ip_entries_chunk, results|
results.concat get_ip_entries(ip_entries_chunk)
end
duration = Time.now - start_time
found, not_found = ip_entries.partition { |entry| entry.ip }
puts "\nFound:\n\n"; puts found.map(&:to_s); puts "\n\n"
puts "Not Found:\n\n"; puts not_found.map(&:to_s); puts "\n\n"
stats = {
duration: duration,
domain_count: ip_entries.size,
found_count: found.size,
not_found_count: not_found.size,
}
ap stats
end
main

How do I mock a method of 'Socket' using MiniTest?

I have a short method which takes no arguments and returns a hash of local IP addresses in string form as keys with true as the value:
def self.local_ips
# puts 'Getting ip addresses for this computer...'
local_ips = {}
Socket.ip_address_list.each do |address|
ip_string = address.ip_address
ip_string.sub!(/\%.*$/, '') # Removes interface from end of some IP's
local_ips[IPAddr.new(ip_string).to_s] = true
end
return local_ips
end
This works correctly, correctly being defined by it is working as expected, but I would like to have a spec which tests this method. The problem is, Socket.ip_address_list will return differently on each machine it is on. I was wondering if there was a way while using MiniTest to mock the response of Socket.ip_address_list so that the method can be tested on any machine without fear of failure due to differences related to the local machine.
Any help would be greatly appreciated.
Thanks!
It wasn't mock which needed to be used but rather a stub:
fake_ip = Struct.new :ip_address
fake_ips = %w[ woot ]
Socket.stub :ip_address_list, lambda { fake_ips.map { |s| fake_ip.new s } } do
Socket.ip_address_list.map { |ip| ip.ip_address }
end
# => ["woot"]

Ruby: Parse API Response

I am trying to geht this script to run: http://dysinger.net/2008/10/13/using-amazon-ec2-metadata-as-a-simple-dns but dosnt work because it is using an old amazon sdk version, i rewrote it to use the new one:
#!/usr/bin/env ruby
require "rubygems"
require "aws-sdk"
%w(optparse rubygems aws-sdk resolv pp).each {|l| require l}
options = {}
parser = OptionParser.new do |p|
p.banner = "Usage: hosts [options]"
p.on("-a", "--access-key USER", "The user's AWS access key ID.") do |aki|
options[:access_key_id] = aki
end
p.on("-s",
"--secret-key PASSWORD",
"The user's AWS secret access key.") do |sak|
options[:secret_access_key] = sak
end
p.on_tail("-h", "--help", "Show this message") {
puts(p)
exit
}
p.parse!(ARGV) rescue puts(p)
end
if options.key?(:access_key_id) and options.key?(:secret_access_key)
puts "127.0.0.1 localhost"
AWS.config(options)
AWS::EC2.new(options)
answer = AWS::EC2::Client.new.describe_instances
answer.reservationSet.item.each do |r|
r.instancesSet.item.each do |i|
if i.instanceState.name =~ /running/
puts(Resolv::DNS.new.getaddress(i.privateDnsName).to_s +
" #{i.keyName}.ec2 #{i.keyName}")
end
end
end
else
puts(parser)
exit(1)
end
What this should do is outputing a new /etc/hosts file with my ec2 instances in it.
And i get a response =D, but answer is a hash and therefore i get the
error undefined method `reservationSet' for #<Hash:0x7f7573b27880>.
And this is my problem, since i dont know Ruby at all ( All I was doing was reading Amazon Documentation and playing around so i get an answer ). Somehow in the original example this seemed to work. I suppose that back then, the API did not return a hash, anyway...how can i iterate through a hash like above, to get this to work?
This code may help you:
answer = AWS::EC2::Client.new.describe_instances
reservations = answer[:reservation_set]
reservations.each do |reservation|
instances = reservation[:instances_set]
instances.each do |instance|
if instance[:instance_state][:name] == "running"
private_dns_name = instance[:private_dns_name]
key_name = instance[:key_name]
address = Resolv::DNS.new.getaddress(private_dns_name)
puts "{address} #{key_name}.ec2 #{key_name}"
end
end
end
Generally change your code from using methods with names e.g. item.fooBarBaz to using a hash e.g. item[:foo_bar_baz]
When you're learning Ruby the "pp" command is very useful for pretty-printing variables as you go, such as:
pp reservations
pp instances
pp private_dns_name

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/

Reverse DNS in 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.

Resources