Я пытаюсь получить доступ к информации веб-сайта программным способом, но как на Java, так и на Python не удается разрешить имя хоста. Если я укажу IP-адрес, ошибка изменится на TLSV1_UNRECOGNIZED_NAME. Однако этот веб-сайт можно разрешить без каких-либо дополнительных действий через любой браузер.
Я просмотрел здесь множество потенциальных решений, но для Python написано, что эта проблема должна была быть решена в версии 2.7 или 2.8, но я использую 3.10 и все еще получаю эту ошибку. В Java утверждается, что это известная ошибка, но представленные решения, такие как удаление заголовка SNI с помощью параметра компиляции или передача пустого массива имен хостов в HTTPSURLConnection для отмены создания заголовка SNI, не решают проблему. Я также попробовал установить пользовательский агент на Mozilla, как предложено в ответе здесь, но это тоже ничего не изменило.
Я уверен, что на веб-сайте есть что-то необычное, но он не принадлежит мне, поэтому я не могу многое проверить о его конфигурации.
В частности, веб-сайт, который я пытаюсь увидеть:
URL -> https://epic7db.com/heroes
IP -> 157.230.84.20
DNS Lookup -> https://www.nslookup.io/domains/epic7db.com/webservers/
При локальном использовании nslookup я получаю следующее:
nslookup epic7db.com
Server: UnKnown
Address: 10.0.0.1
Non-authoritative answer:
Name: epic7db.com
Address: 157.230.84.20
Любая помощь будет оценена по достоинству, поскольку я, по сути, швыряю вещи в стену, чтобы посмотреть, что прилипнет к этому моменту.
Обновлено: Добавление примеров кода. Питон:
import requests
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36'} # This is chrome, you can set whatever browser you like
url = 'https://epic7db.com'
a = requests.get(url,headers)
print(a.content)
Котлин с использованием Java HttpsUrlConnection:
import http.SSLSocketFactoryWrapper
import java.net.URL
import javax.net.ssl.*
fun main() {
HttpsURLConnection.setDefaultHostnameVerifier { hostName, session -> true }
val url = URL("https://epic7db.com")
val sslParameters = SSLParameters()
val sniHostNames: MutableList<SNIHostName> = ArrayList<SNIHostName>(1)
// sniHostNames.add(SNIHostName(url.getHost()))
sslParameters.setServerNames(sniHostNames as List<SNIServerName>?)
val wrappedSSLSocketFactory: SSLSocketFactory =
SSLSocketFactoryWrapper(SSLContext.getDefault().socketFactory, sslParameters)
HttpsURLConnection.setDefaultSSLSocketFactory(wrappedSSLSocketFactory)
val conn = url.openConnection() as HttpsURLConnection
conn.hostnameVerifier = HostnameVerifier { s: String?, sslSession: SSLSession? -> true }
println(String(conn.inputStream.readAllBytes()))
}
Предлагаемый вспомогательный класс в Kotlin/Java:
package http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class SSLSocketFactoryWrapper extends SSLSocketFactory {
private final SSLSocketFactory wrappedFactory;
private final SSLParameters sslParameters;
public SSLSocketFactoryWrapper(SSLSocketFactory factory, SSLParameters sslParameters) {
this.wrappedFactory = factory;
this.sslParameters = sslParameters;
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port);
setParameters(socket);
return socket;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port, localHost, localPort);
setParameters(socket);
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port);
setParameters(socket);
return socket;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(address, port, localAddress, localPort);
setParameters(socket);
return socket;
}
@Override
public Socket createSocket() throws IOException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket();
setParameters(socket);
return socket;
}
@Override
public String[] getDefaultCipherSuites() {
return wrappedFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return wrappedFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(s, host, port, autoClose);
setParameters(socket);
return socket;
}
private void setParameters(SSLSocket socket) {
socket.setSSLParameters(sslParameters);
}
}
Добавлен пример ошибки имени хоста на Kotlin/Java и Python.
Если код не может разрешить имя домена, но браузер на том же компьютере может это сделать, то браузер, скорее всего, использует либо прокси-сервер, который разрешает имя вместо браузера, либо другой DNS-сервер. Узнайте, что из этого сделано в вашем случае, проверив конфигурацию браузера, а затем сделайте то же самое в своем коде.
«Java утверждает, что это известная ошибка» — Ссылка на авторитетный источник этого факта.
Код Python работает у меня после установки модуля запросов. Что-то еще сломано.
@ThorbjørnRavnAndersen работает с этим адресом и никаких дополнительных изменений?
@BasilBourque Не могу найти вторую ссылку, но одна из них stackoverflow.com/questions/7615645/…
@KM529 да. Точный код, который вы показали. Я предполагаю, что вы на работе, и конфигурация вашей сети не такая, как вы думаете.
Как я могу обойти неразрешенную ошибку имени хоста или нераспознанного имени, используя HTTP (S) в Java или Python?
Скорее всего, это не проблема кода Java или Python1. Неспособность разрешить DNS-имя, скорее всего, вызвана:
(И 3. скорее всего, это будет проявляться в том, что DNS-имя разрешается в неправильный IP-адрес, а не в сбое разрешения. Прочтите о TTL-записях DNS.)
Так что... этого >в программе< не обойти. Вы можете обойти эту проблему, заглянув за пределы программы и выяснив, почему не удается разрешить DNS. Затем вы вносите изменения, чтобы это исправить.
Несколько причин, по которым браузер может работать, а приложение Java или Python — нет (или наоборот):
Браузер может быть настроен на отправку всех HTTP/HTTPS-запросов через прокси. Прокси-сервер может иметь другой доступ к DNS для компьютера, на котором вы запускаете свой код.
В случае с Java (по крайней мере, в Linux) у Java есть собственный способ настройки «локального» DNS-сервера. Обычно это не проблема, но иногда это может означать, что ваше приложение и браузер используют разные серверы.
Когда вы попытались использовать IP-адрес в URL-адресе, вы пропустили этап поиска DNS. Но тут вы столкнулись с проблемой, что сертификаты TLS не будут работать с IP-адресами. (Вы можете «взломать» это, но такие взломы могут иметь потенциально серьезные последствия для безопасности.)
1 - As others have pointed out, there is nothing wrong with the code you added to the question. Indeed, Thorbjørn attests that the code works for him exactly as written!
«невозможно разрешить имя хоста» — покажите код, выдающий эту ошибку.