пятница, 3 июля 2015 г.

Использование isReachable без root прав

Для определения доступности хоста в java можно использовать InetAddress.isReachable(int timeout). Столкнулся c проблемой, что в Linux этот метод не всегда правильно отрабатывает.
В javadoc есть описание:
"A typical implementation will use ICMP ECHO REQUESTs
if the privilege can be obtained, otherwise it will
try to establish a TCP connection on port 7 (Echo) of the destination host."

Наверное в этом месте уже стоит задуматься о каких правах идет речь. Если покопаться в исходниках jdk, то в классе Inet4AddressImpl.c можно найти такой код:

/*
* Let's try to create a RAW socket to send ICMP packets
* This usually requires "root" privileges, so it's likely to fail.
*/
fd = JVM_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd != -1) {
/*
* It didn't fail, so we can use ICMP_ECHO requests.
*/
return ping4(env, fd, &him, timeout, netif, ttl);
}
/*
* Can't create a raw socket, so let's try a TCP socket
*/
fd = JVM_Socket(AF_INET, SOCK_STREAM, 0);

Получается, чтобы доступность хоста определялась  с помощью ICMP запроса, приложение должно быть запущено с правами root. В противном случае будет использовать echo реквест по 7 TCP порту. Вот только не всегда можно запускать приложения от рута, да и не серьезное это решение какое-то. Чтоб создать "сырой" сокет не обязательно ж быть рутом. Достаточно разрешить это делать jvm. Делается это довольно просто:
setcap cap_net_raw=ep /usr/lib/jvm/jdk/bin/java