Ruby sshkit IPv6 string addressing issues - ruby

I'm sure this is an issue on my end that I'm fundamentally misunderstanding. I am using sshkit to connect to and upload some files to a server with IPv6 addressing.
I can SSH into a server with an address like so:
2001:aaa:bbb:cc:dddd:eeee:ffff:gggg
There is no issues until I try to leverage sshkit to do the same. The below example illustrates how the same IPv6 string is represented in an array (which has many addresses) within the script I am running:
on ["[2001:aaa:bbb:cc:dddd:eeee:ffff:gggg]"], in: :parallel do |host|
#some stuff
end
What I end up seeing is:
Exception on host 2001:aaa:bbb:cc:dddd:eeee:ffff caught: getaddrinfo: nodename nor servname provided, or not known
Note the trailing byte seems to be truncated off? Is there a different way I should be formatting that IPv6 address string?

There is a bug with SSHKit at the moment which I am planning to submit a PR for. Essentially the trailing bytes are swallowed as the port number unless specifically denoted.
### Without port number, it parses incorrectly
SSHKit::Host.new '[2001:db8:85a3:8d3:1319:8a2e:370:7348]'
# => #<SSHKit::Host:0x007fc12318d7d8 #keys=[], #local=false, #user=nil, #hostname="2001:db8:85a3:8d3:1319:8a2e:370", #port=7348>
The work around is to specify the port
### With port number it works
SSHKit::Host.new '[2001:db8:85a3:8d3:1319:8a2e:370:7348]:22'
# => #<SSHKit::Host:0x007fc1231e46c8 #keys=[], #local=false, #user=nil, #hostname="2001:db8:85a3:8d3:1319:8a2e:370:7348", #port=22>

Related

Shell (bash) snmpset script tells Error in packet WrongLength

Hi i have written the bash script for downloading configuration from switches and save it to TFTP server.
snmpset -v 2c -c Zaloznik 192.168.50.22 1.3.6.1.4.1.1991.1.1.2.1.6.0 s test_skript.cfg 1.3.6.1.4.1.1991.1.1.2.1.66.0 x C0A846D2 1.3.6.1.4.1.1991.1.1.2.1.9.0 i 22 >> /dev/null;
But it always tell me this:
Error in packet. Reason: wrongLength (The set value has an illegal
length from what the agent expects) Failed object:
iso.3.6.1.4.1.1991.1.1.2.1.66.0
C0A846D2 is a HEX format of ip 192.168.70.210.
Don't you know how to fix it ? Please help, i have tried many combinations and nothing working.
Thanks.
Problem solved, there was a problem with switches that want to have an info about
type of ip address (ipv4 or ipv6), then ip address of tftp, file name and after that he can send config files to tftp.
So i have to add another snmp OID (ip address type) into the script and then it works.

Hostnames resolution fails with "unknown host" error for hostnames containing utf-8 characters

I am trying to ping a hostname "win-2k12r2-addc.阿伯测阿伯测ad.hai.com" from a linux client.
I see that DNS requests go over the wire with hostname being sent in utf-8 format
and i get a response from the DNS server also with the correct IP address.
But ping fails with the following error :
ping: unknown host win-2k12r2-addc.阿伯测阿伯测ad.hai.com
If i add an entry into /etc/hosts, it works fine
I have the following entries in /etc/hosts when it works.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
127.0.0.1 localhost ava-dev
::1 localhost
10.141.33.93 win-2k12r2-addc.阿伯测阿伯测ad.hai.com
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The /etc/nsswitch.conf file has the following entries for hosts.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hosts: files dns
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I somewhat suspect that getaddrInfo() call fails when we try to resolve the address i.e it is not able to handle the DNS responses correctly for hostnames
containing unicode characters.
Has anyone faced this issue before ?
Or has anyone tried resolving a unicode hostname from a linux client ?
The reason i m suspecting getaddrinfo() is because of the following.
Apart from ping, i m trying the following ldap command to the same host and it fails with the below mentioned error
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ldapsearch -d 255 -x -h win-2k12r2-addc.阿伯测阿伯测ad.hai.com
ldap_create
ldap_url_parse_ext(ldap://win-2k12r2-addc.%E9%98%BF%E4%BC%AF%E6%B5%8B%E9%98%BF%E4%BC%AF%E6%B5%8Bad.hai.com)
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP win-2k12r2-addc.阿伯测阿伯测ad.hai.com:389
ldap_connect_to_host: getaddrinfo failed: Name or service not known
ldap_err2string
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
In both the scenarios (ping / ldap), i see the DNS query request going to the DNS server and the correct response from the DNS server back to the linux client.
The following is the value of the hostname sent in the DNS query
win-2k12r2-addc.\351\230\277\344\274\257\346\265\213\351\230\277\344\274\257\346\265\213ad.hai.com: type A, class IN
It looks like you are trying to use UTF-8 or unicode within the DNS system while the DNS system really doesn't like that. It wants ascii (See RFCs 5890, 5891, 5892, 5893 - but mostly 5891). Escaping the utf-8 characters does not turn them into the required ascii encoding, called punycode (prefixed by "xn--"). You want to use the version of your IDN that has punycode instead of the UTF-8:
ping win-2k12r2-addc.xn--ad-tl3ca3569aba8944eca.hai.com

parse /etc/hosts for ip and hostname with puppet, and transpose into /etc/sysconfig/network-scripts/ifcfg-eth0

I am kicking machines with cobbler to install redhat 6 and also put in hostname.
I am managing my machines with /etc/hosts file right now (may switch to dns in the future) and would like to have puppet (or anything that will work) parse through /etc/hosts and find my ip address and hostname (based on the hostname supplied by cobbler at kickstart). The trick is that some machines have multiple IP's and hostnames in the /etc/hosts file, like such:
# Maintenance Network
192.168.80.192 testsrv01-maint
192.168.80.193 testsrv02-maint
192.168.80.194 testsrv03-maint
# Lights Out Network
192.168.120.192 testsrv01-ilo
192.168.120.193 testsrv02-ilo
192.168.120.194 testsrv03-ilo
# Primary Data Network
192.168.150.192 testsrv01-pri
192.168.150.193 testsrv02-pri
192.168.150.194 testsrv03-pri
# Secondary Data Network
192.168.200.192 testsrv01-sec
192.168.200.193 testsrv02-sec
192.168.200.194 testsrv03-sec
I need to capture each ip and hostname pair (in a line) and transpose into /etc/sysconfig/network-scripts/ifcfg-eth* (eth1, eth2, eth3, ...). Puppet will need to create as many ifcfg-eth* files as there are matches in /etc/hosts for the hostname.
I just need puppet to append the $IP and $hostname to the ifcfg-eth file, the rest of the content is common.
So how would I get 4x ifcfg-eth files for 'testsrv01', with puppet?
Puppet is a very ill fit for this task. This calls for a script, which Puppet does not support. Puppet allows you to declare a piece of machine state that can portably enforced on different platforms.
The task is simple enough for a Shell or Perl Script.
With Puppet, it would entail the following scripts
writing custom facts to retrieve each address / hostname pair
devising a defined type to render such a pair into an ethX file
You'd possibly even need to generate the respective manifest, so that the appropriate interface index is chosen for each address.
All things considered, you would not make use of Puppet's strengths and suffer some of its weaknesses.

Why can't I see SNMP Traps coming in?

I'm attempting to use Ruby SNMP to capture SNMP traps from various devices. In order to test them I'm attempting to send them from my laptop using the 'snmptrap' command. I can see that the traps are being sent and arriving at my server (the server is the manager) in packet captures, as well as in the 'snmptrapd' utility when I run it. I'm using the following example code exactly as it is, in the demo from the documentation to set up a TrapListener.
require 'snmp'
require 'logger'
log = Logger.new(STDOUT)
m = SNMP::TrapListener.new do |manager|
manager.on_trap_default do |trap|
log.info trap.inspect
end
end
m.join
I'm sending an SNMPv2c trap, and nothing ever appears on the screen...
Here is the command I'm using to send a test SMTP trap, in the even that it's useful:
snmptrap -v 2c -c public hostname_goes_here SNMP-NOTIFICATION-MIB::snmpNotifyType SNMPv2-MIB::sysLocation
Any suggestions appreciated! Thanks!
I was stuck on this for a long time as well. It turns out that by default, Traplistener only opens ports on 127.0.0.1. To make it listen on ALL interfaces on the port you specified (or default port 162), specify a :Host option. '0' makes it listen on ALL interfaces, or you can provide an IP address.
log = Logger.new(STDOUT)
m = SNMP::TrapListener.new(:Host => 0) do |manager|
manager.on_trap_default do |trap|
log.info trap.inspect
end
end
m.join

Getting the Hostname or IP in Ruby on Rails

I'm in the process of maintaining a Ruby on Rails app and am looking for an easy way to find the hostname or IP address of the box I'm on (since it's a VM and new instances may have different hostnames or IP addresses). Is there a quick and easy way to do this in Ruby on Rails?
Edit: The answer below is correct but the clarification Craig provided is useful (see also provided link in answer):
The [below] code does NOT make a
connection or send any packets (to
64.233.187.99 which is google). Since UDP is a stateless protocol connect()
merely makes a system call which
figures out how to route the packets
based on the address and what
interface (and therefore IP address)
it should bind to. addr() returns an
array containing the family (AF_INET),
local port, and local address (which
is what we want) of the socket.
Hostname
A simple way to just get the hostname in Ruby is:
require 'socket'
hostname = Socket.gethostname
The catch is that this relies on the host knowing its own name because it uses either the gethostname or uname system call, so it will not work for the original problem.
Functionally this is identical to the hostname answer, without invoking an external program. The hostname may or may not be fully qualified, depending on the machine's configuration.
IP Address
Since ruby 1.9, you can also use the Socket library to get a list of local addresses. ip_address_list returns an array of AddrInfo objects. How you choose from it will depend on what you want to do and how many interfaces you have, but here's an example which simply selects the first non-loopback IPV4 IP address as a string:
require 'socket'
ip_address = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
From coderrr.wordpress.com:
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
# irb:0> local_ip
# => "192.168.0.127"
Try this:
host = `hostname`.strip # Get the hostname from the shell and removing trailing \n
puts host # Output the hostname
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 return 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 the Addrinfo methods used to filter the required interface address.
Simplest is host_with_port in controller.rb
host_port= request.host_with_port
This IP address used here is Google's, but you can use any accessible IP.
require "socket"
local_ip = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
Similar to the answer using hostname, using the external uname command on UNIX/LINUX:
hostname = `uname -n`.chomp.sub(/\..*/,'') # stripping off "\n" and the network name if present
for the IP addresses in use (your machine could have multiple network interfaces),
you could use something like this:
# on a Mac:
ip_addresses = `ifconfig | grep 'inet ' | grep -v 127.0.0.1 | cut -d' ' -f 2`.split
=> ['10.2.21.122','10.8.122.12']
# on Linux:
ip_addresses = `ifconfig -a | grep 'inet ' | grep -v 127.0.0.1 | cut -d':' -f 2 | cut -d' ' -f 1`.split
=> ['10.2.21.122','10.8.122.12']
The accepted answer works but you have to create a socket for every request and it does not work if the server is on a local network and/or not connected to the internet. The below, I believe will always work since it is parsing the request header.
request.env["SERVER_ADDR"]
Put the highlighted part in backticks:
`dig #{request.host} +short`.strip # dig gives a newline at the end
Or just request.host if you don't care whether it's an IP or not.
You will likely find yourself having multiple IP addresses on each machine (127.0.0.1, 192.168.0.1, etc). If you are using *NIX as your OS, I'd suggest using hostname, and then running a DNS look up on that. You should be able to use /etc/hosts to define the local hostname to resolve to the IP address for that machine. There is similar functionality on Windows, but I haven't used it since Windows 95 was the bleeding edge.
The other option would be to hit a lookup service like WhatIsMyIp.com. These guys will kick back your real-world IP address to you. This is also something that you can easily setup with a Perl script on a local server if you prefer. I believe 3 lines or so of code to output the remote IP from %ENV should cover you.
io = IO.popen('hostname')
hostname = io.readlines
io = IO.popen('ifconfig')
ifconfig = io.readlines
ip = ifconfig[11].scan(/\ \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\ /)
The couple of answers with require 'socket' look good. The ones with request.blah_blah_blah
assume that you are using Rails.
IO should be available all the time. The only problem with this script would be that if ifconfig is output in a different manor on your systems, then you would get different results for the IP. The hostname look up should be solid as Sears.
try: Request.remote_ip
remote_ip()
Determine originating IP address. REMOTE_ADDR is the standard but will
fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
HTTP_X_FORWARDED_FOR are set by proxies so check for these if
REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- delimited
list in the case of multiple chained proxies; the last address which
is not trusted is the originating IP.
Update:
Oops, sorry I misread the documentation.

Resources