Идентификация сервера SSH не получена - Тупик Handshake [SSHJ]
У нас возникли проблемы с попыткой реализовать пул SftpConnections для нашего приложения.
В настоящее время мы используем SSHJ
(Schmizz) в качестве транспортной библиотеки и сталкиваемся с проблемой, которую мы просто не можем имитировать в нашей среде разработки (но ошибка продолжает проявляться случайным образом в производстве, иногда через три дня, иногда после 10 минут).
Проблема заключается в том, что при попытке отправить файл через SFTP поток блокируется в методе init
из класса schmizz 'TransportImpl
:
@Override
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
throws TransportException {
connInfo = new ConnInfo(remoteHost, remotePort, in, out);
try {
if (config.isWaitForServerIdentBeforeSendingClientIdent()) {
receiveServerIdent();
sendClientIdent();
} else {
sendClientIdent();
receiveServerIdent();
}
log.info("Server identity string: {}", serverID);
} catch (IOException e) {
throw new TransportException(e);
}
reader.start();
}
isWaitForServerIdentBeforeSendingClientIdent для нас ЛОЖЬ, поэтому, прежде всего, клиент (мы) отправляем нашу идентификацию, как показано в журналах:
"Идентификатор клиента String: blabla"
Затем он поворачивается для receiveServerIdent
:
private void receiveServerIdent() throws IOException
{
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
while ((serverID = readIdentification(buf)).isEmpty()) {
int b = connInfo.in.read();
if (b == -1)
throw new TransportException("Server closed connection during identification exchange");
buf.putByte((byte) b);
}
}
Нить никогда не получает контроль, так как сервер никогда не отвечает своей личностью. Кажется, что код застрял в этом цикле While. Никаких тайм-аутов или исключений SSH не выбрасывается, мой клиент просто ждет навсегда, и поток заходит в тупик.
Это метод readIdentification
:
private String readIdentification(Buffer.PlainBuffer buffer)
throws IOException {
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
if (ident.isEmpty()) {
return ident;
}
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
Похоже, что входной поток ConnectionInfo никогда не получает данные для чтения, как если бы сервер закрывал соединение (даже если, как было сказано ранее, исключение не генерируется).
Я попытался смоделировать эту ошибку, насытив согласование, закрывая сокеты при подключении, используя conntrack, чтобы убить установленные соединения, пока выполняется рукопожатие, но не везет вообще, поэтому любая помощь будет ВЫСОКО оценили.
:)
Ответы
Ответ 1
Я уверен, что следующий код создает проблему:
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
if (ident.isEmpty()) {
return ident;
}
Если IdentificationStringParser.parseIdentificationString() возвращает пустую строку, она будет возвращена методу вызывающего. Метод вызывающего абонента будет продолжать вызывать while ((serverID = readIdentification(buf)).isEmpty())
, поскольку строка всегда пуста. Единственный способ разбить цикл будет, если вызов int b = connInfo.in.read();
возвращает -1... но если сервер продолжает отправлять данные (или пересылает данные), это условие никогда не выполняется.
Если это так, я бы добавил какой-то искусственный способ обнаружить это как:
private String readIdentification(Buffer.PlainBuffer buffer, AtomicInteger numberOfAttempts)
throws IOException {
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
numberOfAttempts.incrementAndGet();
if (ident.isEmpty() && numberOfAttempts.intValue() < 1000) { // 1000
return ident;
} else if (numberOfAttempts.intValue() >= 1000) {
throw new TransportException("To many attempts to read the server ident").
}
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
Таким образом, вы, по крайней мере, подтвердите, что это так, и можете больше понять, почему .parseIdentificationString() возвращает пустую строку.