2017-05-03 1 views
1

Ich versuche, einen NTLMProxyHandler in Netty zu implementieren, der den NTLM-Nachrichtenaustausch ausführen und den Client mit einem Webproxy authentifizieren kann.Netty ProxyHandler writeAndFlush schreibt keine Antwort auf Server

Der NTLMProxyHandler erweitert Nettys ProxyHandler-Klasse. Aus diesem Grund wird vom Proxy-Handler eine initiale HTTP-Anfrage ausgelöst, die den von mir erstellten Proxy-Proxy-Server erreicht. Der Proxy-Server liest diese Anforderung und antwortet mit einer 407-Proxy-Authentifizierungs-erforderlichen Antwort.

Der NTLMProxyHandler liest diese Antwort auf der Clientseite und bereitet eine neue NTLM Type1Message vor und schreibt die Antwort erneut auf den Server. Das Problem, mit dem ich konfrontiert bin, ist, dass diese Anfrage nie an meinen Proxy-Server gesendet wird, obwohl der Erfolgs-Handler des Channels Future aufgerufen wird.

Ich habe Netty-Pakete in der Protokollierung aktiviert, kann aber nicht herausfinden, warum nur die Antwort, die das zweite Mal vom NTLM-Proxy-Handler geschrieben wurde, verloren gegangen ist.

Ich habe versucht, die Netty ProxyHandler sendToProxyServer (msg) sowie die channelHandlerCtx von channelRead() übergeben. In beiden Fällen ist writeAndFlush beendet, aber die Antwort erreicht den Server nie und der Server überschreitet das Zeitlimit.

Hat jemand den channelHandlerCtx verwendet, um eine Antwort auf den Server zurückzuschreiben und einen ähnlichen Nachrichtenaustausch durchzuführen?

  1. Warum ist die erste Anfrage von Ntlm Proxy-Handler -> Server erfolgreich, aber nicht aufeinander folgenden Antworten geschrieben von diesem Ntlm Proxy-Handler.
  2. Ich sehe auch beim Debuggen, dass, selbst wenn ich den Proxy-Server beim Schreiben der NTLMMessage1 heruntergefahren, die WriteAndFlush Zukunft ist immer noch erfolgreich. Warum sollte writeAndFlush in diesem Fall erfolgreich sein?

Alle Zeiger sind sehr hilfreich. Vielen Dank !

NTLMProxyHandler.java

import io.netty.buffer.ByteBuf; 
    import io.netty.buffer.Unpooled; 
    import io.netty.channel.ChannelHandlerContext; 
    import io.netty.channel.ChannelPipeline; 
    import io.netty.handler.codec.http.DefaultFullHttpRequest; 
    import io.netty.handler.codec.http.DefaultFullHttpResponse; 
    import io.netty.handler.codec.http.FullHttpResponse; 
    import io.netty.handler.codec.http.HttpClientCodec; 
    import io.netty.handler.codec.http.HttpContent; 
    import io.netty.handler.codec.http.HttpHeaderNames; 
    import io.netty.handler.codec.http.HttpHeaders; 
    import io.netty.handler.codec.http.HttpMethod; 
    import io.netty.handler.codec.http.HttpResponse; 
    import io.netty.handler.codec.http.HttpResponseStatus; 
    import io.netty.handler.codec.http.HttpVersion; 
    import io.netty.handler.codec.http.LastHttpContent; 
    import io.netty.handler.proxy.ProxyConnectException; 
    import jcifs.ntlmssp.Type1Message; 
    import jcifs.ntlmssp.Type2Message; 
    import jcifs.ntlmssp.Type3Message; 
    import jcifs.smb.NtlmContext; 
    import jcifs.smb.NtlmPasswordAuthentication; 
    import jcifs.util.Base64; 

    import org.slf4j.Logger; 
    import org.slf4j.LoggerFactory; 

    import java.net.InetSocketAddress; 
    import java.net.SocketAddress; 


    public class NTLMProxyHandler extends AbstractProxyHandler { 

     private String userName; 
     private String password; 
     private final static String DOMAIN  = "CORP"; 
     public static final String NTLM_Prefix = "NTLM"; 

     private static final Logger logger = LoggerFactory.getLogger(NTLMProxyHandler.class); 

     private static int NTLMV2_FLAGS_TYPE3 = 0xa2888205; 
     private HttpResponseStatus status; 
     private HttpResponse response; 

     private NtlmPasswordAuthentication ntlmPasswordAuthentication; 
     private NtlmContext ntlmContext; 
     private final HttpClientCodec codec = new HttpClientCodec(); 

     public NTLMProxyHandler(SocketAddress proxyAddress) { 
      super(proxyAddress); 
     } 

     public NTLMProxyHandler(SocketAddress proxyAddress, String domain, String username, String password) { 
      super(proxyAddress); 
      setConnectTimeoutMillis(50000); 
      this.userName = username; 
      this.password = password; 
      ntlmPasswordAuthentication = new NtlmPasswordAuthentication(DOMAIN, username, password); 
      ntlmContext = new NtlmContext(ntlmPasswordAuthentication, true); 
     } 

     @Override 
     public String protocol() { 
      return "http"; 
     } 

     @Override 
     public String authScheme() { 
      return "ntlm"; 
     } 

     protected void addCodec(ChannelHandlerContext ctx) throws Exception { 
      ChannelPipeline p = ctx.pipeline(); 
      String name = ctx.name(); 
      p.addBefore(name, (String)null, this.codec); 
     } 

     protected void removeEncoder(ChannelHandlerContext ctx) throws Exception { 
      this.codec.removeOutboundHandler(); 
     } 

     protected void removeDecoder(ChannelHandlerContext ctx) throws Exception { 
      this.codec.removeInboundHandler(); 
     } 

     @Override 
     protected Object newInitialMessage(ChannelHandlerContext channelHandlerContext) throws Exception { 
      InetSocketAddress raddr = this.destinationAddress(); 
      String rhost; 
      if(raddr.isUnresolved()) { 
       rhost = raddr.getHostString(); 
      } else { 
       rhost = raddr.getAddress().getHostAddress(); 
      } 

      String host = rhost + ':' + raddr.getPort(); 
      DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false); 
      req.headers().set(HttpHeaderNames.HOST, host); 
      req.headers().set("connection", "keep-alive"); 

// This initial request successfully reaches the server ! 
      return req; 
     } 

     @Override 
     protected boolean handleResponse(ChannelHandlerContext channelHandlerContext, Object o) throws Exception { 

      if (o instanceof HttpResponse) { 
       response = (HttpResponse) o; 

      } 
      boolean finished = o instanceof LastHttpContent; 

      if(finished) { 
       status = response.status(); 
       logger.info("Status: " + status); 

       if (!response.headers().isEmpty()) { 
        for (String name: response.headers().names()) { 
         for (String value: response.headers().getAll(name)) { 
          logger.debug("Header: " + name + " = " + value); 
         } 
        } 
       } 
       if(status.code() == 407) { 
        negotiate(channelHandlerContext, response); 
       } 
       else if(status.code() == 200){ 
        logger.info("Client: NTLM exchange complete. Authenticated !"); 
       } 
       else { 
        throw new ProxyConnectException(this.exceptionMessage("status: " + this.status)); 
       } 
      } 

      return finished; 
     } 

     private void negotiate(ChannelHandlerContext channelHandlerContext, HttpResponse msg) throws Exception{ 
      String ntlmHeader = msg.headers().get(HttpHeaderNames.PROXY_AUTHENTICATE); 

      if(ntlmHeader.equalsIgnoreCase("NTLM")){ 
       logger.info("Client: Creating NTLM Type1Message"); 
       //Send Type1Message 
       byte[] rawType1Message = ntlmContext.initSecContext(new byte[]{}, 0, 0); 
       Type1Message type1Message = new Type1Message(rawType1Message); 

       FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); 
       String proxyAuthHeader = Base64.encode(type1Message.toByteArray()); 
       logger.info("Setting proxyAuthHeader = " + proxyAuthHeader); 
       response.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, proxyAuthHeader); 

       ByteBuf byteBuf = Unpooled.buffer(rawType1Message.length); 
       byteBuf.writeBytes(response.content()); 

//This is where the response is lost and never reaches the proxy server 
       sendToProxyServer(byteBuf); 
       // channelHandlerContext.writeAndFlush(response.content)); 

      } else if (ntlmHeader.contains(NTLM_Prefix)) { 
       logger.info("Client: Creating NTLM Type3Message"); 
       //Send Type3 Message 

      } 
     } 
    } 
+0

Haben Sie einen ChannelFutureListener an die ChannelFuture angehängt, die vom Aufruf writeAndFlush (...) zurückgegeben wurde? Dadurch können Sie sehen, ob der Schreibvorgang aus irgendeinem Grund fehlgeschlagen ist. –

+0

@NormanMaurer Ja, ich tat und wenn der Listener in OperationComplete() benachrichtigt wird, sehe ich io.netty.util.concurrent.DefaultPromise # SUCCESS. Tatsächlich fügt Nettys ProxyHandler, den ich erweitere, auch einen writeListener während sendtoProxyServer an und es gelingt auch. – ram

Antwort

0

ich endlich herausgefunden, das Problem. Der NTLM-Proxy-Handler, der auf die Nachricht des Proxys reagiert, sendet eine FullHTTPResponse anstelle von FullHTTPRequest. Sieht so aus, als ob Nettys Pipeline die als Antwort geschriebenen Daten verworfen hätte und dies wurde nicht in den Protokollen angezeigt.

DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false); 
req.headers().set(HttpHeaderNames.HOST, host); 
req.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, "type3message"); 

sendToProxyServer(req); 
+0

Dies sollte einen Fehler verursacht haben. Seltsam –

Verwandte Themen