SocketTlsHandshake

TLS Handshakeプロトコルを介して指定されたホストへの安全なTLS(SSL)接続を開始します。ハンドシェイク中、クライアントとサーバは接続パラメータ、つまり適用されるプロトコルのバージョンとデータの暗号化方式について合意します。

bool  SocketTlsHandshake(
  int           socket,             // ソケット
  const string host                 // ホストアドレス
  );

パラメータ

socket

[in] SocketCreate関数で返されるソケットハンドルです。無効なハンドルが渡されると、5270 (ERR_NETSOCKET_INVALIDHANDLE)が_LastErrorに書かれます。

host

[in] 安全な接続が確立されているホストのアドレス

戻り値

成功の場合はtrue、それ以外の場合はfalse

注意事項

安全な接続の前に、プログラムはSocketConnectを使用してホストとの標準TCP接続を確立する必要があります。

安全な接続が失敗すると、エラー5274 (ERR_NETSOCKET_HANDSHAKE_FAILED)が _LastErrorに書かれます。

ポート443に接続する場合はこの関数を呼び出す必要はありません。これは、安全なTLS(SSL)接続に使用される標準のTCPポートです。

この関数は独自のスレッド内で実行されるエキスパートアドバイザーやスクリプトのみから呼び出すことが出来ます。指標から呼び出すと、GetLastError()はエラー4014「Function is not allowed for call(関数呼び出しの許可がありません)」を返します。

例:

//+------------------------------------------------------------------+
//|                                           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
//+------------------------------------------------------------------+
//| 安全な接続に手動で切り替える                                            |
//+------------------------------------------------------------------+
bool TlsHandshake(int socket)
 {
//--- サーバの挨拶を取得する
  string rsp;
 
  if(!RecvString(socket,rsp))
    return(false);
//--- サーバに挨拶する
  if(!SendString(socket,"EHLO my.domain.com\r\n"))
    return(false);
//--- サポートされているコマンドのリストを含むサーバ応答を取得する
  if(!RecvString(socket,rsp))
    return(false);
//--- 挨拶をプリントする
  Print("SERVER: ",rsp);
//--- 安全でない接続から TLS を使用して安全な接続に切り替えることをサーバーに通知する
  if(!SendString(socket,"STARTTLS\r\n"))
    return(false);
//--- サーバの応答を取得する
  if(!RecvString(socket,rsp))
    return(false);
//--- この例では、TLSへの切り替えの準備状況に関するサーバの応答(「Ready to start TLS」)は確認しない
 
//--- TLS Handshakeプロトコルを介して指定されたホストへの安全なTLS (SSL)接続を開始する
  if(SocketTlsHandshake(socket,InpTimeout))
    return(true);
 
  Print("SocketTlsHandshake() failed. Error ",GetLastError());
  return(false);
 }
//+------------------------------------------------------------------+
//| スクリプトプログラム開始関数                                              |
//+------------------------------------------------------------------+
void OnStart(void)
 {
//--- ソケットを作成し、そのハンドルを取得する
  int socket=SocketCreate();
 
  if(socket==INVALID_HANDLE)
    {
    Print("SocketCreate() failed. Error ",GetLastError());
    return;
    }
//--- ポート経由でサーバーに接続する
  if(!SocketConnect(socket,SERVER,PORT,10000))
    {
    Print("SocketConnect() failed. Error ",GetLastError());
    }
  else
    {
    //--- 安全でない接続が確立された
    PrintFormat("%s connection has been established to %s:%d",(PORT==443 ? "A secured" : "An unsecured"),SERVER,PORT);
    //--- 安全な接続に切り替える
    if(PORT!=443 && TlsHandshake(socket))
       {
        PrintFormat("Unsecured connection to %s:%d switched to secured",SERVER,PORT);
        //--- 接続が証明書で保護されている場合は、そのデータを表示する
        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);
          }
       }
    }
//--- 使用後はソケットを閉じる</t1>
  SocketClose(socket);
  /*
  結果:
  An unsecured connection has been established to smtp.gmail.com:587
  SERVER: 220 smtp.gmail.com ESMTP a640c23a62f3a-a9b1f298319sm82305866b.105 - gsmtp
 
  SERVER: 250-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
 
  SERVER: 220 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
    Print: d6be8af229b5329cd3d4c2789c02aa94f89b421c
    Expiration: 2024.12.30 08:25:30
  */
 }
//+------------------------------------------------------------------+
//| サーバに文字列を送信する                                               |
//+------------------------------------------------------------------+
bool SendString(int socket,const string str)
 {
//--- 文字列を文字配列に変換する
  uchar data[];
  int   size=StringToCharArray(str,data,0,str.Length(),CP_UTF8);
//--- ソケットにデータを送信する
  ResetLastError();
  if(SocketSend(socket,data,size)==size)
    return(true);
//--- データ送信エラー
  Print("Failed to send data to server. Error ",GetLastError());
  return false;
 }
//+------------------------------------------------------------------+
//| サーバから文字列を取得する                                              |
//+------------------------------------------------------------------+
bool RecvString(int socket,string& result,uint timeout_ms=1000)
 {
//--- ソケットにデータが現れるのを待機する
  ulong wait_time_end=GetMicrosecondCount()+timeout_ms*1000;
 
  while(!SocketIsReadable(socket))
    {
    Sleep(10);
    //--- データの待機中にタイムアウトしたため、応答としてNULLを返す
    if(wait_time_end<GetMicrosecondCount())
       {
        Print("ERROR: No response from server");
        return(false);
       }
    }
//--- ソケットからデータを読む
  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));
//--- 読み取ったデータを文字列にコピーする
  if(size)
    resp+=CharArrayToString(data,0,size,CP_UTF8);
//--- 文字列が空の場合はエラー
  if(!resp.Length())
    {
    Print("ERROR: No response from server");
    return(false);
    }
//--- 文字列を返す
  result=resp;
  return(true);
 }