2015-05-20 14 views
6

Ho listener di socket asincratico multi-thread. Voglio verificare se la richiesta è sicura o meno. Ma voglio verificare che nel metodo AcceptCallBack non ReceiveCallBack.Verifica se la richiesta arriva tramite HTTP o HTTPS nell'ascoltatore di socket

Lo farò perché voglio che il mio codice funzioni sia per HTTP che per HTTPS. Se la richiesta proviene da HTTPS, procederò con un SslStream autenticato invece che con socket non elaborato.

Ecco il mio codice:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Threading; 
using System.Text; 

namespace LearnRequestType 
{ 
    class StackOverFlow 
    { 
     private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false); 

     private void StartListening() 
     { 
      IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9002); 

      if (localEndPoint != null) 
      { 
       Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

       if (listener != null) 
       { 
        listener.Bind(localEndPoint); 
        listener.Listen(10); 

        Console.WriteLine("Socket listener is running..."); 

        listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 
       } 
      } 
     } 

     private void AcceptCallback(IAsyncResult ar) 
     { 
      _manualResetEvent.Set(); 

      Socket listener = (Socket)ar.AsyncState; 

      Socket handler = listener.EndAccept(ar); 

      StateObject state = new StateObject(); 
      state.workSocket = handler; 

      // I want to understand if request comes from HTTP or HTTPS before this line. 
      handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 

      listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 
     } 

     private void ReceiveCallback(IAsyncResult result) 
     { 
      StateObject state = (StateObject)result.AsyncState; 
      Socket handler = state.workSocket; 

      string clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString(); 

      int numBytesReceived = handler.EndReceive(result); 

      if (!handler.Connected) 
      { 
       handler.Close(); 
       return; 
      } 

      // Read incoming data... 
      if (numBytesReceived > 0) 
      { 
       state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, numBytesReceived)); 

       // Read incoming data line by line. 
       string[] lines = state.sb.ToString().Split('\n'); 

       if (lines[lines.Length - 1] == "<EOF>") 
       { 
        // We received all data. Do something... 

       } 
       else 
       { 
        // We didn't receive all data. Continue reading... 
        handler.BeginReceive(state.buffer, 0, state.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), state); 
       } 
      } 
     } 
    } 
} 

public class StateObject 
{ 
    public Socket workSocket = null; 
    public const int BufferSize = 256; 
    public byte[] buffer = new byte[BufferSize]; 
    public StringBuilder sb = new StringBuilder(); 
} 

Se cambio AcceptCallBack metodo e StateObject Classe così:

private void AcceptCallback(IAsyncResult ar) 
{ 
    _manualResetEvent.Set(); 

    Socket listener = (Socket)ar.AsyncState; 

    Socket handler = listener.EndAccept(ar); 

    try 
    { 
     sslStream = new SslStream(new NetworkStream(handler, true)); 

     // try to authenticate 
     sslStream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true); 

     state.workStream = sslStream; 
     state.workStream.ReadTimeout = 100000; 
     state.workStream.WriteTimeout = 100000; 

     if (state.workStream.IsAuthenticated) 
     { 
      state.workStream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state); 
     } 
    } 
    catch (IOException ex) 
    { 
     // ıf we get handshake failed due to an unexpected packet format, this means incoming data is not HTTPS 
     // Continue with socket not sslstream 
     state.workSocket = handler; 
     handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 
    } 

    StateObject state = new StateObject(); 
    state.workStream = handler; 

    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 

    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); 
} 

public class StateObject 
{ 
    public Socket workSocket = null; 
    public SslStream workStream = null; 
    public const int BufferSize = 1024; 
    public byte[] buffer = new byte[BufferSize]; 
    public StringBuilder sb = new StringBuilder(); 
} 

posso decidere se il tipo di dati in ingresso è HTTP o HTTPS, ma se si tratta di HTTP verrà gestito da catch catch ogni volta, quindi diminuirà le prestazioni dell'applicazione.

C'è un altro modo?

risposta

4

Se ho capito correttamente, si dispone di una singola porta in cui il client può utilizzare HTTP o HTTPS per connettersi e si desidera sapere immediatamente come è stata effettuata la richiesta, prima che i dati vengano trasferiti.

Non c'è modo di saperlo prima di ricevere dati dal client. HTTP e HTTPS sono protocolli su TCP, non funzionano su livelli di protocollo inferiori, quindi non c'è alcun flag o nulla che possa dire quale protocollo viene utilizzato. Anche HTTPS è solo il normale flusso HTTP avvolto in un flusso TLS/SSL.

Sarà necessario leggere i dati e determinare in base a quale protocollo viene utilizzato. O devi avere porte separate per HTTP e HTTPS, il che renderebbe questo banale.

per rilevare se si tratta di TLS/SSL si potrebbe peek pochi byte e vedere cosa viene in. The TLS specification dice il Cliente Ciao pacchetto inizia con la versione del protocollo, che viene inviato in due uint8 s. Poiché una richiesta HTTP avrà sempre il verbo come prima, si potrebbe facilmente verificare se un paio dei primi byte sono caratteri o meno e quindi provare SSLStream se non lo è.

Si noti inoltre che se si avvia SSLStream sul socket, potrebbe leggere dal socket, che consumerebbe l'inizio della richiesta HTTP e non si poteva semplicemente gestirlo normalmente.

Quindi nel tuo Accetta uso di richiamata o meno così:

Socket handler = listener.EndAccept(ar); 
byte[] tmp = new byte[2]; 
handler.Receive(tmp, 0, 2, SocketFlags.Peek); 
if (!Char.IsLetter((char)tmp[0]) || !Char.IsLetter((char)tmp[1])) 
{ 
    // Doesn't start with letters, so most likely not HTTP 
} else { 
    // Starts with letters, should be HTTP 
} 

Se volete fare effettivamente assicurarsi che sia TLS/SSL, potreste controllare this question on SO

+0

Grazie per la risposta. In realtà posso verificare se è HTTP o HTTPS facendo così. Controlla la mia domanda modificata. Ma qui il problema è provare a catturare i blocchi. –

+0

@OrkunBekar Aggiunta di informazioni su come avere una buona intuizione se si tratta di HTTP o meno. –

+0

Come posso vedere la versione del protocollo? È memorizzato all'interno di un oggetto nel metodo AcceptCallBack? Dove avrò questi due valori uint8? –

Problemi correlati