2017-01-13 27 views
12

Mamy problemy z próbą wdrożenia puli SftpConnections dla naszej aplikacji.Identyfikacja serwera SSH nigdy nie została odebrana - impasu Handshake [SSHJ]

Obecnie korzystamy z biblioteki transportowej SSHJ (Schmizz) i napotykamy problem, którego po prostu nie możemy zasymulować w naszym środowisku programistycznym (ale błąd pojawia się losowo w produkcji, czasami po trzech dniach, czasami po zaledwie 10 minutach).

Problem jest, gdy próbuje wysłać plik przez SFTP, wątek zostanie zamknięty w init metody z schmizz”TransportImpl Klasa:

@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 jest FAŁSZ dla nas, więc przede wszystkim klient (my) wysłać naszą identyfikację, jak pojawia się w dziennikach:

"Client tożsamości String: blabla"

Potem kolej na 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); 
     } 
    } 

Nić nie dostaje kontrolę z powrotem, ponieważ serwer nie odpowiada ze swoją tożsamością. Wygląda na to, że kod utknął w tej pętli While. Nie ma limitu czasu ani wyjątków SSH, mój klient po prostu czeka na zawsze, a wątek zostaje zablokowany.

Jest w readIdentification metody badaniem impl:

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; 
} 

Wygląda InputStream ConnectionInfo nigdy nie pobiera dane do odczytu, jak wtedy, gdy serwer zamknął połączenie (nawet jeśli, jak powiedziałem wcześniej, nie jest wyjątek) .

Próbowałem zasymulować ten błąd, nasycając negocjacje, zamykając gniazda podczas łączenia, używając conntrack, aby zabić ustanowione połączenia podczas wykonywania uścisku dłoni, ale bez żadnego szczęścia, więc każda pomoc byłaby bardzo cenna, aby uzyskać .

:)

+0

Czy próbowałeś ustawić wartość flagi 'waitForServerIdent' na wartość true. Z jakim typem serwera SSH łączysz się? –

+0

Czy zbadałeś zrzut wątku? –

+0

Hiery Nomus: Nie ma różnicy w zmianie zamówienia i jego serwerze SFTP. :) Vladislav: Zrzut wątku pokazuje zakleszczenie we wspomnianej części: nie ma odpowiedzi od serwera, więc program zostaje zablokowany na zawsze ... to nie jest problem z kodem. –

Odpowiedz

1

założę Poniższy kod tworzy problem:

String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString(); 
if (ident.isEmpty()) { 
    return ident; 
} 

Jeśli IdentificationStringParser.parseIdentificationString() zwraca pusty ciąg znaków, zostanie on zwrócony do sposobu rozmówcy. Metoda wywołująca będzie nadal wywoływać while ((serverID = readIdentification(buf)).isEmpty()), ponieważ ciąg jest zawsze pusty. Jedynym sposobem na przerwanie pętli byłoby wywołanie int b = connInfo.in.read(); zwraca -1 ... ale jeśli serwer wysyła dane (lub ponowne wysłanie danych) ten warunek nigdy nie jest spełniony.

Jeśli jest to przypadek Dodam jakiś sztuczny sposób, aby wykryć jak:

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; 
} 

ten sposób byś przynajmniej potwierdzić, że jest to przypadek i można kopać dalej dlaczego .parseIdentificationString() zwraca pusty ciąg.

Powiązane problemy