How to get ip address from your server?


#1

I mean by IP, for example the eth0 or other adapter, not the localhost.

ip=Socket.ip_address_list.detect{|intf| intf.ipv4_private?}

addr_infos = Socket.getifaddrs

These ruby functions do not exist in Crystal.

Crystal::System seems to get CPU count and hostname but no IP information.


#2

Could these methods help?
https://crystal-lang.org/api/0.27.0/IPSocket.html#local_address-instance-method
https://crystal-lang.org/api/0.27.0/IPSocket.html#remote_address-instance-method


#3

Could these methods help?

I think not because IPSocket does not bind to the net interfaces. And will only return 0.0.0.0 this way.

The only other way that i know is “hostname -I” with a shell exec but that is kind off dirty way for doing this.


#4

In unix-land, I’d do run ifconfig -a and then scan for ip addresses in the output. hostname -I does not work on macOS or FreeBSD. I assume -I gives you the IP address of the interface which matches the hostname, but note that a machine can easily have multiple IP addresses. Depending on your goal, your program might need to know all of those addresses.

Of course, ifconfig -a probably won’t work on MS-Windows.


#5

We should probably have a Crystal API for that. It just needs to be implemented.


#6

I assume -I gives you the IP address of the interface which matches the hostname

Yes, it will gives you something like this:

hostname -I
192.168.222.129 172.18.0.1

We should probably have a Crystal API for that. It just needs to be implemented.

Indeed, i figured this was already part of Crystal but after going over the documentation and not finding anything, trying out a few ruby tricks, it became clear there is no automated way to find those IPs.


#7

For the people who need it, here is a more hackish way to get the task done ( Linux only ):

output = IO::Memory.new
Process.run("hostname", args: {"-I"}, output: output)
output.close
puts output.to_s.split()

The split is needed if you have more then one result.


#8

A little remark, it could be done with shorter puts `hostname -I` syntax :slightly_smiling_face:


#9

How does the Ruby implementation work then?


#10

See the first post … Those are some of the answers that i found for Ruby.

ip=Socket.ip_address_list.detect{|intf| intf.ipv4_private?}

I think this one was in Ruby 2.x introduced.


#11

I also trying the same objective.

With some additional LibC binding, I had got network interface list of the local machine and IPv4/IPv6/MAC addresses for each network interface.

Sample code is https://github.com/arcage/net_sample.cr
Note: It is tested only on the CentOS7 and macOS 10.14.

But, I think more LibC bindings for other compile environments are necessary.

And, I hope the compiler supports that function officially.


#12

This should go in a shard, really. I’m not sure this fits in the stdlib. It’s not a very common usecase.


#13

This should go in a shard, really. I’m not sure this fits in the stdlib. It’s not a very common usecase.

If you bind on 0.0.0.0 because you deploy on a cluster system with micro services ( Crystal and Go bread and butter ) and you do not know each external IP internally. There are times that your code may need to know what IPs the http server really binds to… In my case it is needed for the micro services to know from themselves where the brother services are located ( what requires self discovery of the external IP and publishing to the swarm ).

This question is not so rare, that Ruby also included several methods in the standard framework.

Shards are good and well but in a lot of cases when a shard get published, it does not mean it gets maintained. crystalshards.xyz tracking is already full of shards that have not seen updates in ages with pull or issue requests hanging in limbo.


#14

A friendly neighbour sysadmin here. IP addresses are harder to do right than it would seem at the first sight. If you bind on 0.0.0.0 then you tell the kernel to accept the packets on all interfaces no matter the IP. The answer then depends on the question. Do you need to known the destination address from the packet you’ve received? Do you want all the addresses on all the interfaces at this particular moment (they can and do change all the time)? Anyway, shelling out to hostname is rarely the way to go. On linux you can parse /proc/self/net/fib_trie, or at least ip addr output. So, to recap:

you do not know each external IP internally

this is impossible to determine in general case, consider NATs for example

need to know what IPs the http server really binds to

if you bind to 0.0.0.0, you really really bind to 0.0.0.0

where the brother services are located

you may want to look at existing service discovery solutions, like consul