//+------------------------------------------------------------------+
//| 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 핸드셰이크 프로토콜을 사용하여 지정된 호스트에 대한 보안 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);
}
}
}
//--- 사용 후 소켓 종료
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);
}
|