InetAddress.getCanonicalHostName() возвращает IP вместо имени хоста

Я искал, как выполнить поиск IP в Java при переполнении стека, но ответы соответствуют тому, что я уже делаю, и не решают мою проблему.

Вот мой код:

public void printHostname( String ip ) {
    System.out.println( InetAddresses.forString( ip ).getCanonicalHostName( ) );
}

InetAddresses это просто служебный класс из библиотеки гуавы, чтобы получить InetAdress,

Проблема: этот код работает, как и ожидалось, с некоторыми IP-адресами, а не с некоторыми другими.

Рабочий пример

Например, для IP 157.55.39.29 вывод:

msnbot-157-55-39-29.search.msn.com

Этот результат кажется правильным в соответствии с Linux host команда:

> host 157.55.39.29
29.39.55.157.in-addr.arpa domain name pointer msnbot-157-55-39-29.search.msn.com.

Неработающий пример

Для IP 123.125.71.75, host команда возвращает:

> host 123.125.71.75
75.71.125.123.in-addr.arpa domain name pointer baiduspider-123-125-71-75.crawl.baidu.com.

Но вывод моего кода Java:

123.125.71.75

в то время как ожидаемый результат должен быть

baiduspider-123-125-71-75.crawl.baidu.com

Javadoc метода getCanonicalHostName говорит:

Возвращает:
полное доменное имя для этого IP-адреса или, если проверка не разрешена операцией, текстовое представление IP-адреса.

но я уверен, что это не проблема с проверкой безопасности... или я не понимаю, что не так.

Есть ли у вас предложения, чтобы объяснить это поведение? У вас есть обходной путь?

РЕДАКТИРОВАТЬ #1

При поиске решения я попытался пошагово отладить реализацию в JDK:

// first lookup the hostname
host = nameService.getHostByAddr(addr.getAddress());

/* check to see if calling code is allowed to know
 * the hostname for this IP address, ie, connect to the host
 */
if (check) {
    SecurityManager sec = System.getSecurityManager();
    if (sec != null) {
       sec.checkConnect(host, -1);
    }
}

/* now get all the IP addresses for this hostname,
 * and make sure one of them matches the original IP
 * address. We do this to try and prevent spoofing.
 */

 InetAddress[] arr = InetAddress.getAllByName0(host, check);

В этом коде переменная host содержит правильное значение, но последний оператор вызывает getAllByName0бросает UnknownHostExceptionкоторый обрабатывается путем возврата только запрошенного IP. Исключение выдается внутренним методом.getAddressesFromNameServiceс сообщением:"java.net.UnknownHostException: baiduspider-123-125-71-75.crawl.baidu.com"

Я не знаю почему.

Могу ли я получить host значение переменной, минуя внутреннее исключение?

2 ответа

Решение

Проблема заключается в том, что java.net.InetAdress имеет определенную процедуру против ip-спуфинга.

Сначала он разрешает имя в IP-адрес (а). Это отлично работает. В вашем случае результат - два IP-адреса. InetAdress затем проверяет, соответствует ли (хотя бы один из) этим адресам исходное имя ввода.

Если они этого не делают, он просто возвращает исходный IP-адрес. На следующем рисунке показана ситуация после проверки baiduspider-123-125-71-75.crawl.baidu.com

Отладчик после проверки `оригинального обратного просмотра ip -> name -> dns lookup name name -> dns lookup name>

Примечание: IP-адреса разрешены getAllByName0 такие же как через nslookupа именно:

nslookup baiduspider-123-125-71-75.crawl.baidu.com
Server:     192.168.2.1
Address:    192.168.2.1#53

Non-authoritative answer:
Name:   baiduspider-123-125-71-75.crawl.baidu.com
Address: 62.157.140.133
Name:   baiduspider-123-125-71-75.crawl.baidu.com
Address: 80.156.86.78

Решением будет использование библиотеки dnsjava. Он пропускает проверку спуфинга и поэтому работает нормально.

Пример dnsjava:

String addr = Address.getHostName(InetAddress.getByName("123.125.71.75"));выходы так же, как и ожидалось baiduspider-123-125-71-75.crawl.baidu.com

Отказ от ответственности: Поскольку я являюсь Java-разработчиком, а не экспертом по безопасности, я не совсем осведомлен о последствиях безопасности использования поддельного IP-адреса.

Я не много копался в этом, поэтому я не знаю, почему это происходит, но некоторые онлайн-инструменты ( например, этот), которые проверяют работоспособность DNS-серверов, показывают, что у них есть некоторые проблемы, которые могут быть или не быть связаны.

Как сказал @jah, Java пытается перепроверить, чтобы увидеть, есть ли у имени хоста тот IP, который он сказал. Исключение выдается в нативный код при попытке сделать это. На самом деле, в моем случае, пытаясь проверить в командной строке, nslookup не может получить ip из имени, что указывает на некоторую конфигурацию, предотвращающую это на DNS-сервере (может быть, нарочно? Я тоже не эксперт в DNS),

Когда это работает:

$ nslookup msnbot-157-55-39-29.search.msn.com
Server:         192.168.1.1
Address:        192.168.1.1#53

Non-authoritative answer:
Name:   msnbot-157-55-39-29.search.msn.com
Address: 157.55.39.29

Когда это не работает:

$ nslookup baiduspider-123-125-71-75.crawl.baidu.com
Server:         192.168.1.1
Address:        192.168.1.1#53

** server can't find baiduspider-123-125-71-75.crawl.baidu.com: NXDOMAIN

Когда это работает:

$ getent hosts msnbot-157-55-39-29.search.msn.com
157.55.39.29    msnbot-157-55-39-29.search.msn.com

Когда это не так:

$ getent hosts baiduspider-123-125-71-75.crawl.baidu.com
$

В качестве альтернативы вы можете использовать поставщика услуг DNS для JNDI. В документации есть пример, но я оставлю рабочий фрагмент для тестирования:

String[] octets = "123.125.71.75".split("\\.");
String host = String.join(".", octets[3], octets[2], octets[1], octets[0], "in-addr.arpa");

Properties props = new Properties();
props.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
DirContext dirContext = new InitialDirContext(props);

Attributes attrs = dirContext.getAttributes(host, new String[] {"PTR"});

System.out.println(attrs.get("PTR").get());
Другие вопросы по тегам