SocketTlsHandshake

Inicia una conexión TLS (SSL) protegida con el host indicado según el protocolo TLS Handshake. Durante el Handshake, el cliente y el servidor coordinan los parámetros de conexión: la versión del protocolo utilizado y el método de cifrado de los datos.

bool  SocketTlsHandshake(
   int           socket,               // socket
   const string  host                  // dirección del host
   );

Parámetros

socket

[in]  Manejador del socket retornado por la función SocketCreate. Al transmitir un manejador incorrecto, en _LastError se registra el error 5270 (ERR_NETSOCKET_INVALIDHANDLE).

host

[in]  Dirección del host con el que se establece la conexión protegida.

Valor retornado

Retorna true en caso de éxito, de lo contrario, false.

Observaciones

Antes de la conexión protegida, el programa deberá establecer una conexión TCP normal con el host con la ayuda de SocketConnect.

Si se da un error al establecer la conexión protegida, en _LastError se registrará el error 5274 (ERR_NETSOCKET_HANDSHAKE_FAILED).

La llamada de esta función no será necesaria si la conexión se realiza con el puerto 443. Se trata del puerto TCP estándar utilizado para las conexiones TLS (SSL) protegidas.

Solo se puede llamar la función desde los expertos y scripts, puesto que funcionan en su propio flujo de ejecución. Si se llama desde el indicador, GetLastError() retornará el error 4014 — "La función de sistema no está permitida para la llamada".

Ejemplo:

//+------------------------------------------------------------------+
//|                                           SocketTlsHandshake.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com
#property version     "1.00"
 
#define   SERVER    "smtp.gmail.com"
#define   PORT      587
//+------------------------------------------------------------------+
//| Cambio manual de una conexión insegura a una conexión segura     |
//+------------------------------------------------------------------+
bool TlsHandshake(int socket)
  {
//--- recibimos el saludo del servidor
   string rsp;
 
   if(!RecvString(socket,rsp))
      return(false);
//--- saludamos al servidor
   if(!SendString(socket,"EHLO my.domain.com\r\n"))
      return(false);
//--- recibimos una respuesta del servidor con una lista de comandos admitidos
   if(!RecvString(socket,rsp))
      return(false);
//--- imprimimos el saludo
   Print("SERVER: ",rsp);
//--- informamos al servidor de que queremos pasar de una conexión insegura a una segura mediante TLS
   if(!SendString(socket,"STARTTLS\r\n"))
      return(false);
//--- obtenemos la respuesta del servidor
   if(!RecvString(socket,rsp))
      return(false);
//--- En el ejemplo, no comprobamos la respuesta del servidor sobre su preparación para cambiar a TLS ('Ready to start TLS')
 
//--- iniciar una conexión segura TLS (SSL) con el host especificado utilizando el protocolo TLS Handshake
   if(SocketTlsHandshake(socket,SERVER))
      return(true);
 
   Print("SocketTlsHandshake() failed. Error ",GetLastError());
   return(false);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart(void)
  {
//--- creamos un socket y obtenemos su handle
   int socket=SocketCreate();
 
   if(socket==INVALID_HANDLE)
     {
      Print("SocketCreate() failed. Error ",GetLastError());
      return;
     }
//--- nos conectamos al servidor SERVER en el puerto PORT
   if(!SocketConnect(socket,SERVER,PORT,10000))
     {
      Print("SocketConnect() failed. Error ",GetLastError());
     }
   else
     {
      //--- se ha establecido una conexión insegura
      PrintFormat("%s connection has been established to %s:%d",(PORT==443 ? "A secured" : "An unsecured"),SERVER,PORT);
      //--- cambiamos a una conexión segura
      if(PORT!=443 && TlsHandshake(socket))
        {
         PrintFormat("Unsecured connection to %s:%d switched to secured",SERVER,PORT);
         //--- si la conexión está protegida por un certificado, mostraremos sus datos
         string   subject,issuer,serial,thumbprint;
         datetime expiration;
 
         if(SocketTlsCertificate(socket,subject,issuer,serial,thumbprint,expiration))
           {
            Print("TLS certificate:");
            Print("   Owner:      ",subject);
            Print("   Issuer:     ",issuer);
            Print("   Number:     ",serial);
            Print("   Print:      ",thumbprint);
            Print("   Expiration: ",expiration);
           }
        }
     }
//--- cerramos el socket después de usarlo
   SocketClose(socket);
   /*
   resultado:
   An unsecured connection has been established to smtp.gmail.com:587
   SERVER220 smtp.gmail.com ESMTP a640c23a62f3a-a9b1f298319sm82305866b.105 - gsmtp
 
   SERVER250-smtp.gmail.com at your service, [37.193.40.122]
   250-SIZE 35882577
   250-8BITMIME
   250-STARTTLS
   250-ENHANCEDSTATUSCODES
   250-PIPELINING
   250-CHUNKING
   250 SMTPUTF8
 
   SERVER220 2.0.0 Ready to start TLS
 
   SocketTlsHandshake(): A secure connection to smtp.gmail.com:587 is now established
   TLS certificate:
      Owner:  /CN=smtp.gmail.com
      Issuer:  /C=US/O=Google Trust Services/CN=WR2
      Number:     1f:f4:db:2a:5a:e6:dc:52:0a:4c:05:ce:81:cc:c3:f7
      Printd6be8af229b5329cd3d4c2789c02aa94f89b421c
      Expiration2024.12.30 08:25:30
   */
  }
//+------------------------------------------------------------------+
//| Envía una cadena al servidor                                     |
//+------------------------------------------------------------------+
bool SendString(int socket,const string str)
  {
//--- convertimos la cadena en un array de caracteres
   uchar data[];
   int   size=StringToCharArray(str,data,0,str.Length(),CP_UTF8);
//--- enviamos los datos al socket
   ResetLastError();
   if(SocketSend(socket,data,size)==size)
      return(true);
//--- error de envío de datos
   Print("Failed to send data to server. Error ",GetLastError());
   return false;
  }
//+------------------------------------------------------------------+
//| Получает строку с сервера                                        |
//+------------------------------------------------------------------+
bool RecvString(int socket,stringresult,uint timeout_ms=1000)
  {
//--- esperamos a que los datos aparezcan en el socket
   ulong wait_time_end=GetMicrosecondCount()+timeout_ms*1000;
 
   while(!SocketIsReadable(socket))
     {
      Sleep(10);
      //--- el tiempo de espera de los datos ha expirado: retornamos NULL como respuesta
      if(wait_time_end<GetMicrosecondCount())
        {
         Print("ERROR: No response from server");
         return(false);
        }
     }
//--- leemos los datos del socket
   uchar  data[128];
   uint   size=0;
   string resp=NULL;
 
   do
     {
      uchar b[1];
      int   n=SocketRead(socket,b,1,1000);
 
      if(n < 0)
         break;
 
      if(n)
        {
         data[size++]=b[0];
 
         if(size==data.Size())
           {
            resp += CharArrayToString(data,0,data.Size(),CP_UTF8);
            size = 0;
           }
        }
     }
   while(SocketIsReadable(socket));
//--- copiamos los datos leídos en la cadena
   if(size)
      resp+=CharArrayToString(data,0,size,CP_UTF8);
//--- si la cadena está vacía, tendremos un error
   if(!resp.Length())
     {
      Print("ERROR: No response from server");
      return(false);
     }
//--- entregamos la cadena
   result=resp;
   return(true);
  }