Ruby Sockets: Error (EINVAL) while trying to bind a sending socket to a port - ruby

The connect method fails giving an EINVAL when I am trying to connect from blue.example.edu to green.example.edu
I do not get this error when I connect from blue to blue itself.
The Ruby documentation under connect suggests
Errno::EINVAL - the address length used for the sockaddr is not a
valid length for the address family or there is an invalid family in
sockaddr
But since the same parameter is working correctly when I try connecting blue to another port on itself, this might be wrong ?
Here is the code snippet:
MY_NAME = Socket.gethostname
SERVER_NAME = "blue" # Socket.gethostname returns 'blue' on the server
SERVER_IP = Socket::getaddrinfo(SERVER_NAME, 'www', nil, Socket::SOCK_STREAM)[0][3]
#--> Gives me A.B.C.27 (see note on ipconfig below)
SERVER_LISTENING_PORT = 34900
MY_NAME = Socket.gethostname
MY_PORT_FOR_REQUESTING_SERVER = 40000
# other stuff
#CREATE
client_socket = Socket::new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
client_socket_addr = Socket.pack_sockaddr_in(MY_PORT_FOR_REQUESTING_SERVER, MY_NAME)
#BIND
client_socket.bind(client_socket_addr)
#CONNECT
server_addr = Socket.pack_sockaddr_in(SERVER_LISTENING_PORT, SERVER_NAME)
i_tried_with_ip_address_also = Socket.pack_sockaddr_in(SERVER_LISTENING_PORT, SERVER_IP)
#The line below raises the error
client_socket.connect(server_addr)
# more code below
Please note that hitting ifconfig on blue yields the same A.B.C.27 what the SERVER_IP contains
Under eth1 - inet addr:A.B.C.27, Bcast:A.B.C.255 Mask:255.255.255.0
I'm guessing this is related to the issue ?
Using Ruby 1.8.7 on Ubuntu machines
Please advice.
EDIT:
I used TCPSocket as suggested by zed_0xff in order to get this working temporarily.
However I wish to know why the regular socket way is not working.
The only difference I can see is that using the Socket class, I am binding the client_socket_addr to a port from which I wish to send my data.
TCPSocket sends data from a random port to the specified listening port on the server.
What do I do if I wish to bind the sending to a specific port ?
EDIT2: Changed question title from:
Ruby Sockets: Errno::EINVAL: Invalid argument - connect(2)
So the answer from #zed_0xff has helped me tackle one of my problems of actually establishing a connection between the client and the server (Thank you!)
However, with that method, TCPSocket chooses a random sending port for sending data. Trying to binding the sending port to a particular port number fails.
PS: Please let me know if modifying the question name is against the community guidelines. Since my issue was only partially resolved, I edited the title it here itself and explained the edits in the "Edit" sections.

Getting EINVAL for bind() indicates that the address to bind to might still be in use.
To get around this
either use a different address (ip-address and/or port)
or if you are wanting to reuse a address, set the socket option SO_REUSEADDR on the socket prior to using it
or do not bind ... ;-)

maybe you should try with TCPSocket instead?
require 'socket'
s = TCPSocket.new SERVER_NAME, SERVER_LISTENING_PORT
while line = s.gets # Read lines from socket
puts line # and print them
end
s.close # close socket when done
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/TCPSocket.html

I had the following in my specs:
url = "http://image.generator.example.com/100x100"
URI.parse(url).open
I was getting this error because the image placeholder generator had ceased working. Replacing the URL fixed things.

Related

Platform independent method of getting IP address of primary network interface

I was wondering if there was a way to get the primary network interface's (in my case, wlan0) IP address, regardless of platform, in Ruby.
In Python3, I had the following function:
def getIPAddress():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
Which I could call with:
print("My IP address: " + getIPAddress())
Which would work on Windows, Linux and Mac.
I am sure there is a way of doing this in Ruby, but I haven't found anything yet.
So I was wondering if you knew any way of doing this?
Thanks!
Looking at the documentation for Ruby's Socket class, your Python code can be translated almost 1:1 to Ruby:
require "socket"
def get_ip_address
s = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM)
s.connect(Socket.pack_sockaddr_in(80, "8.8.8.8"))
Socket.unpack_sockaddr_in(s.getsockname)[1]
end
puts "My IP address: #{get_ip_address}"

How to check connection to mongodb is active?

What method should I call on Mongo::Client object to check that connection to the database server is active?
require 'mongo'
host = '127.0.0.1'
port = '27017'
client = Mongo::Client.new("mongodb://#{host}:#{port}")
As mtj suggested, to check if connection is valid/active I am trying to get the list of databases as below:
require 'mongo'
host = '127.0.0.1'
port = '27017'
begin
client = Mongo::Client.new("mongodb://#{host}:#{port}")
client.database_names
rescue Mongo::Auth::Unauthorized, Mongo::Error => e
info_string = "Error #{e.class}: #{e.message}"
puts info_string
end
MongoClient in various languages is implemented as a "lazy stub", so that you will not get an immediate error, if the connection is not possible. Instead, you get the error when a concrete query goes over the line.
As far as I know, the only way to be sure that an actual connection takes place is, to query some data from the server. For example, in our project, we simply read the list of databases upon initialization.
Note: as the query results also may be lazy stubs, make sure to actually read the result in your client ;-)

Ethon - Valid resolve option format

Curl has an option which allows me to specify to which IP a domain should be resolved
e.g. curl --resolve example.com:443:1.2.3.4 https://example.com/foo
to make sure that a very specific server is called
(e.g. when multiple servers have the same vhost with a load balancer usually in front of it and there are multiple applications running on the same port with different vhosts)
How do I set this value when using Ethon? https://github.com/typhoeus/ethon
This is how I'd expect it to work
Ethon::Easy.new(url: "https://example.com/foo", :resolve => "example.com:443:1.2.3.4")
but I'm getting an invalid value exception (I have tried multiple different formats that came to mind)
Ethon::Errors::InvalidValue: The value: example.com:443:1.2.3.4 is invalid for option: resolve.
I took a look at the code but couldn't figure out how I'd have to provide the value - and the documentation on this is a bit scarce
Thanks in advance for any reply that might point me in the right direction
Thanks to i0rek on Github I got the answer I was looking for:
resolve = nil
Ethon::Curl.slist_append(resolve, "example.com:443:1.2.3.4")
e = Ethon::Easy.new(url: "https://example.com/foo", :resolve => resolve)
#=> #<Ethon::Easy:0x007faca2574f30 #url="https://example.com/foo", ...>
Further information can be found here:
https://github.com/typhoeus/ethon/issues/95#event-199961240

How to get my machine's IP address from Ruby without leveraging from other IP address?

I have searched everywhere but their solution requires some form of IP address.
Here are the solutions i have found.
require 'socket'
#METHOD 1
ip = IPSocket.getaddress(Socket.gethostname)
puts ip
#METHOD 2
host = Socket.gethostname
puts host
#METHOD 3(uses Google's address)
ip = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
puts ip
#METHOD 4(uses gateway address)
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 '192.168.1.1', 1
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
ip=local_ip
puts ip
All of them require IP address of someone. Is there a solution that does not use someone else's IP address? Preferably, platform independent.
Isn't the solution you are looking for just:
require 'socket'
addr_infos = Socket.ip_address_list
Since a machine can have multiple interfaces and multiple IP Addresses, this method returns an array of Addrinfo.
You can fetch the exact IP addresses like this:
addr_infos.each do |addr_info|
puts addr_info.ip_address
end
You can further filter the list by rejecting loopback and private addresses, as they are usually not what you're interested in, like so:
addr_infos.reject( &:ipv4_loopback? )
.reject( &:ipv6_loopback? )
.reject( &:ipv4_private? )
This is what I've been using in production for years:
require 'socket'
ip = Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
ip.ip_address
Works great; tested on aws and classical hosting
require 'socket'
Socket::getaddrinfo(Socket.gethostname,"echo",Socket::AF_INET)[0][3]
quite like method 1, actually
As there is no such thing as a default ip-interface to a host (there does not need to be any ip-interface at all actually) all assumptions regarding nameing are vague, do not necessarily hold.
The value returned by gethostname() can be defined independently to any ip-setup, so it does not need to reflect a valid host in terms of a hostname which could be resolved to any ip-address.
From the POSIX system's API's view the only reliabe function to test for the availablily of (ip-)interfaces is the function getifaddrs(), which returns a list of all interfaces along with their parameters.
As it looks as if Ruby's current Socket lib does not provide an interface to it, this (http://rubygems.org/gems/system-getifaddrs) gem based approach does seem to be the only way to go.
parse the output of the ip command?
from https://gist.github.com/henriquemenezes/a99f13da957515023e78aea30d6c0a48
gw = `ip route show`[/default.*/][/\d+\.\d+\.\d+\.\d+/]
or parse the output of the ipconfig command: https://stackoverflow.com/a/12632929/32453

Unsolvable Ruby error

This might be the most stupid thing i've ever seen, i had a broken pipe error with the method send and the ruby class 'Socket', i had this thing 4 days ago and didn't find any thing about it, and i'm kind of going crazy.
I'm almost desperate, i found a broken pipe errors at the internet, but non of them with the send method, or even with the class socket.
my code goes like this:
require 'socket'
sock = Socket.open(Socket::PF_INET,Socket::SOCK_STREAM,Socket::IPPROTO_TCP)
#data = "anyThing"
#addr = pack_sockaddr_in(port, host)
sock.send(#data, 0, #addr)
any help pleas ...
Correct me if I'm wrong, but maybe you need to actually connect to your host before sending data? I see you creating a TCP socket, but no actual connection formed... Does this code work?
require 'socket'
sock = Socket.open(Socket::PF_INET,Socket::SOCK_STREAM,Socket::IPPROTO_TCP)
#data = "anyThing"
#addr = pack_sockaddr_in(port, host)
sock.connect(#addr) #make the connection
sock.send(#data, 0)
Source: http://www.rubycentral.com/pickaxe/lib_network.html
You may also want to try using the TCPSocket class. I haven't used any of this Ruby code, so I'm not used to this particular library; please let me know if I got this all wrong. ;)
require 'socket'
sock = TCPSocket.new(host, port)
#data = "anyThing"
sock.send(#data, 0)
perhaps the other end is closing the connection?

Resources