1

Тема: AHK: Отправка почты через соккеты, TLS и Schannel

Попробовал отправить почту через соккеты с помощью Secure Channel API.
Хорошая статья по теме:
https://web.archive.org/web/20201015234 … h-schannel

server := "smtp.gmail.com"
port := 465   ; can be 587
login := "YourEmail@gmail.com"
password := "YourPassword"
MailTo := "MailTo@gmail.com"
Subject := "Test from Malcev"
EmailText := "This is a test message"

Base64 := Base64(login, password)
data = 
(
EHLO Malcev \r\n
AUTH PLAIN %Base64% \r\n
MAIL FROM:<%login%> \r\n
RCPT TO:<%MailTo%> \r\n
DATA \r\n
Subject: %Subject% \r\n\r\n %EmailText% \r\n.\r\n
QUIT \r\n
)

dwSSPIFlags := (ISC_REQ_SEQUENCE_DETECT := 8) | (ISC_REQ_REPLAY_DETECT := 4) | (ISC_REQ_CONFIDENTIALITY := 16) | (ISC_RET_EXTENDED_ERROR := 16384) | (ISC_REQ_ALLOCATE_MEMORY := 256) | (ISC_REQ_STREAM := 32768)
SEC_E_OK := 0
SEC_I_CONTINUE_NEEDED := 0x00090312
SEC_E_INCOMPLETE_MESSAGE := 0x80090318
SEC_I_INCOMPLETE_CREDENTIALS := 0x00090320
SEC_I_CONTEXT_EXPIRED := 0x00090317
SEC_I_RENEGOTIATE := 0x00090321
SECBUFFER_DATA := 1
SECBUFFER_TOKEN := 2
SECBUFFER_EXTRA := 5
SECBUFFER_STREAM_TRAILER := 6
SECBUFFER_STREAM_HEADER := 7
IO_BUFFER_SIZE := 65536

DllCall("LoadLibrary", "str", "ws2_32.dll", "ptr")
VarSetCapacity(WSADATA, 394 + (A_PtrSize - 2) + A_PtrSize, 0)
DllCall("ws2_32\WSAStartup", "ushort", 0x0202, "ptr", &WSADATA)
socket := DllCall("ws2_32\socket", "int", PF_INET := 2, "int", SOCK_STREAM := 1, "int", 0)
sin_port := DllCall("ws2_32\htons", "ushort", port, "ushort")
VarSetCapacity(addrinfo, 16+4*A_PtrSize, 0)
NumPut(AF_INET := 2, addrinfo, 4, "int")
NumPut(SOCK_STREAM := 1, addrinfo, 8, "int")
NumPut(IPPROTO_TCP := 6, addrinfo, 12, "int")
DllCall("ws2_32\getaddrinfo", "astr", server, "ptr", 0, "ptr", &addrinfo, "ptr*", addrinfoOut)
addrinfoOut_ai_addr := NumGet(addrinfoOut+0, 16+2*A_PtrSize, "ptr")
sin_addr := NumGet(addrinfoOut_ai_addr+0, 4, "uint")
VarSetCapacity(sockaddr_in, 16, 0)
NumPut(AF_INET := 2, sockaddr_in, 0, "short")   ; sin_family
NumPut(sin_port, sockaddr_in, 2, "ushort")   ; sin_port
NumPut(sin_addr, sockaddr_in, 4, "uint")   ; sin_addr
DllCall("ws2_32\connect", "int", socket, "ptr", &sockaddr_in, "int", 16)
if (port != 465)
{
   ReceivedDataSize := 4096
   VarSetCapacity(ReceivedData, ReceivedDataSize, 0)
   DllCall("ws2_32\recv", "int", socket, "ptr", &ReceivedData, "int", ReceivedDataSize, "int", 0)
   msgbox % strget(&ReceivedData, "utf-8")
   string := "EHLO Malcev`r`n"
   DllCall("ws2_32\send", "int", socket, "astr", string, "int", strlen(string), "int", 0)
   VarSetCapacity(ReceivedData, ReceivedDataSize, 0)
   DllCall("ws2_32\recv", "int", socket, "ptr", &ReceivedData, "int", ReceivedDataSize, "int", 0)
   msgbox % strget(&ReceivedData, "utf-8")
   string := "starttls`r`n"
   DllCall("ws2_32\send", "int", socket, "astr", string, "int", strlen(string), "int", 0)
   VarSetCapacity(ReceivedData, ReceivedDataSize, 0)
   DllCall("ws2_32\recv", "int", socket, "ptr", &ReceivedData, "int", ReceivedDataSize, "int", 0)
   msgbox % strget(&ReceivedData, "utf-8")
}

DllCall("LoadLibrary","str", "Secur32.dll", "ptr")
g_pSSPI := DllCall("Secur32\InitSecurityInterface", "ptr")
VarSetCapacity(hCreds, 2*A_PtrSize, 0)
VarSetCapacity(SchannelCred, A_PtrSize*6+40, 0)
NumPut(SCHANNEL_CRED_VERSION := 4, SchannelCred, 0, "uint")   ; dwVersion
NumPut(SP_PROT_TLS1_2_CLIENT := 0x800, SchannelCred, A_PtrSize*6+16, "uint")   ; grbitEnabledProtocols
NumPut(SCH_CRED_NO_DEFAULT_CREDS := 0x00000010, SchannelCred, A_PtrSize*6+32, "uint")   ; dwFlags
DllCall(NumGet(g_pSSPI+3*A_PtrSize), "ptr", 0, "ptr", &(UNISP_NAME := "Microsoft Unified Security Protocol Provider"), "uint", SECPKG_CRED_OUTBOUND := 0x2, "ptr", 0, "ptr", &SchannelCred, "ptr", 0, "ptr", 0, "ptr", &hCreds, "int64*", tsExpiry)   ; AcquireCredentialsHandle
VarSetCapacity(OutBuffers, 8+A_PtrSize, 0)
NumPut(SECBUFFER_TOKEN, OutBuffers, 4, "uint")   ; BufferType
VarSetCapacity(OutBuffer, 8+A_PtrSize, 0)
NumPut(1, OutBuffer, 4, "uint")   ; cBuffers
NumPut(&OutBuffers, OutBuffer, 8, "ptr")   ; pBuffers
VarSetCapacity(hContext, 2*A_PtrSize, 0)
scRet := DllCall(NumGet(g_pSSPI+6*A_PtrSize), "ptr", &hCreds, "ptr", 0, "ptr", &server, "uint", dwSSPIFlags, "uint", 0, "uint", SECURITY_NATIVE_DREP := 16, "ptr", 0, "uint", 0, "ptr", &hContext, "ptr", &OutBuffer, "uint*", dwSSPIOutFlags, "int64*", tsExpiry, "uint")   ; InitializeSecurityContext
if (scRet != SEC_I_CONTINUE_NEEDED)
{
   msgbox InitializeSecurityContext first time error
   exitapp
}
OutBuffers0_cbBuffer := NumGet(OutBuffers, 0, "uint")
OutBuffers0_pvBuffer := NumGet(OutBuffers, 8, "ptr")
if (OutBuffers0_cbBuffer != 0) and (OutBuffers0_pvBuffer != 0)
{
   cbData := DllCall("ws2_32\send", "int", socket, "ptr", OutBuffers0_pvBuffer, "int", OutBuffers0_cbBuffer, "int", 0)
   if (cbData <= 0)
   {
      msgbox ws2_32\send first time error
      exitapp
   }
   DllCall(NumGet(g_pSSPI+16*A_PtrSize), "ptr", OutBuffers0_pvBuffer)   ; FreeContextBuffer
}
fDoRead := 1
GoSub HandshakeLoop
VarSetCapacity(Sizes, 20, 0)
scRet := DllCall(NumGet(g_pSSPI+11*A_PtrSize), "ptr", &hContext, "uint", SECPKG_ATTR_STREAM_SIZES := 4, "ptr", &Sizes, "uint")   ; QueryContextAttributes
if (scRet != SEC_E_OK)
{
   msgbox QueryContextAttributes error
   exitapp
}
Sizes_cbHeader := NumGet(Sizes, 0, "uint")
Sizes_cbTrailer := NumGet(Sizes, 4, "uint")
Sizes_cbMaximumMessage := NumGet(Sizes, 8, "uint")
cbIoBufferLength := Sizes_cbHeader + Sizes_cbMaximumMessage + Sizes_cbTrailer
VarSetCapacity(pbIoBuffer, cbIoBufferLength, 0)
if (port = 465)
   GoSub ReadDecrypt
Loop, parse, data, `n, `r
{
   sendData := StrReplace(A_LoopField, "\r\n", "`r`n")
   GoSub EncryptSend
   msgbox % DecryptedData
}

;DisconnectFromServer
VarSetCapacity(SCHANNEL_SHUTDOWN, 4, 0)
NumPut(1, SCHANNEL_SHUTDOWN, 0, "int")
VarSetCapacity(OutBuffers, 8+A_PtrSize, 0)
NumPut(4, OutBuffers, 0, "uint")   ; cbBuffer
NumPut(SECBUFFER_TOKEN, OutBuffers, 4, "uint")   ; BufferType
NumPut(&SCHANNEL_SHUTDOWN, OutBuffers, 8, "ptr")   ; pbBuffer
VarSetCapacity(OutBuffer, 8+A_PtrSize, 0)
NumPut(1, OutBuffer, 4, "uint")   ; cBuffers
NumPut(&OutBuffers, OutBuffer, 8, "ptr")   ; pBuffers
scRet := DllCall(NumGet(g_pSSPI+10*A_PtrSize), "ptr", &hContext, "ptr", &OutBuffer, "uint")   ; ApplyControlToken
if (scRet != SEC_E_OK)
{
   msgbox ApplyControlToken error
   exitapp
}
VarSetCapacity(OutBuffers, 8+A_PtrSize, 0)
NumPut(SECBUFFER_TOKEN, OutBuffers, 4, "uint")   ; BufferType
VarSetCapacity(OutBuffer, 8+A_PtrSize, 0)
NumPut(1, OutBuffer, 4, "uint")   ; cBuffers
NumPut(&OutBuffers, OutBuffer, 8, "ptr")   ; pBuffers
scRet := DllCall(NumGet(g_pSSPI+6*A_PtrSize), "ptr", &hCreds, "ptr", &hContext, "ptr", 0, "uint", dwSSPIFlags, "uint", 0, "uint", SECURITY_NATIVE_DREP := 16, "ptr", 0, "uint", 0, "ptr", &hContext, "ptr", &OutBuffer, "uint*", dwSSPIOutFlags, "int64*", tsExpiry, "uint")   ; InitializeSecurityContext
if (scRet >= 0x80000000)
{
   msgbox InitializeSecurityContext disconnect error
   exitapp
}
OutBuffers0_cbBuffer := NumGet(OutBuffers, 0, "uint")
OutBuffers0_pvBuffer := NumGet(OutBuffers, 8, "ptr")
if (OutBuffers0_cbBuffer != 0) and (OutBuffers0_pvBuffer != 0)
{
   cbData := DllCall("ws2_32\send", "int", socket, "ptr", OutBuffers0_pvBuffer, "int", OutBuffers0_cbBuffer, "int", 0)
   if (cbData <= 0)
   {
      msgbox ws2_32\send close notify error
      exitapp
   }
   DllCall(NumGet(g_pSSPI+16*A_PtrSize), "ptr", OutBuffers0_pvBuffer)   ; FreeContextBuffer
}
scRet := DllCall(NumGet(g_pSSPI+9*A_PtrSize), "ptr", &hContext, "uint")   ; DeleteSecurityContext
if (scRet != SEC_E_OK)
{
   msgbox DeleteSecurityContext error
   exitapp
}
cbData := DllCall("Ws2_32\closesocket", "int", socket)
if (cbData != 0)
{
   msgbox closesocket error
   exitapp
}
DllCall("ws2_32\WSACleanup")
msgbox done
ExitApp


HandshakeLoop:
VarSetCapacity(IoBuffer, IO_BUFFER_SIZE, 0)
cbIoBuffer := 0
scRet := SEC_I_CONTINUE_NEEDED
loop
{
   if (scRet = SEC_I_CONTINUE_NEEDED) or (scRet = SEC_E_INCOMPLETE_MESSAGE) or (scRet = SEC_I_INCOMPLETE_CREDENTIALS)
   {
      if (cbIoBuffer = 0) or (scRet = SEC_E_INCOMPLETE_MESSAGE)
      {
         if fDoRead
         {
            cbData := DllCall("ws2_32\recv", "int", socket, "ptr", &IoBuffer+cbIoBuffer, "int", IO_BUFFER_SIZE-cbIoBuffer, "int", 0)
            if (cbData = -1)
            {
                msgbox SOCKET_ERROR
                exitapp
            }
            else if (cbData = 0)
            {
                msgbox SEC_E_INTERNAL_ERROR
                exitapp
            }
            cbIoBuffer += cbData
         }
         else
            fDoRead := 1
      }
      VarSetCapacity(InBuffers, (8+A_PtrSize)*2, 0)
      NumPut(cbIoBuffer, InBuffers, 0, "uint")   ; cbBuffer
      NumPut(SECBUFFER_TOKEN, InBuffers, 4, "uint")   ; BufferType
      NumPut(&IoBuffer, InBuffers, 8, "ptr")   ; pbBuffer
      VarSetCapacity(InBuffer, 8+A_PtrSize, 0)
      NumPut(2, InBuffer, 4, "uint")   ; cBuffers
      NumPut(&InBuffers, InBuffer, 8, "ptr")   ; pBuffers
      VarSetCapacity(OutBuffers, 8+A_PtrSize, 0)
      NumPut(SECBUFFER_TOKEN, OutBuffers, 4, "uint")   ; BufferType
      VarSetCapacity(OutBuffer, 8+A_PtrSize, 0)
      NumPut(1, OutBuffer, 4, "uint")   ; cBuffers
      NumPut(&OutBuffers, OutBuffer, 8, "ptr")   ; pBuffers
      scRet := DllCall(NumGet(g_pSSPI+6*A_PtrSize), "ptr", &hCreds, "ptr", &hContext, "ptr", 0, "uint", dwSSPIFlags, "uint", 0, "uint", SECURITY_NATIVE_DREP := 16, "ptr", &InBuffer, "uint", 0, "ptr", 0, "ptr", &OutBuffer, "uint*", dwSSPIOutFlags, "int64*", tsExpiry, "uint")   ; InitializeSecurityContext
      if (scRet = SEC_E_OK) or (scRet = SEC_I_CONTINUE_NEEDED) or ((scRet >= 0x80000000) and (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
      {
         OutBuffers0_cbBuffer := NumGet(OutBuffers, 0, "uint")
         OutBuffers0_pvBuffer := NumGet(OutBuffers, 8, "ptr")
         if (OutBuffers0_cbBuffer != 0) and (OutBuffers0_pvBuffer != 0)
         {
            cbData := DllCall("ws2_32\send", "int", socket, "ptr", OutBuffers0_pvBuffer, "int", OutBuffers0_cbBuffer, "int", 0)
            if (cbData <= 0)
            {
               msgbox ws2_32\send loop error
               exitapp
            }
            DllCall(NumGet(g_pSSPI+16*A_PtrSize), "ptr", OutBuffers0_pvBuffer)   ; FreeContextBuffer
         }
      }
      if (scRet = SEC_E_INCOMPLETE_MESSAGE)
         Continue
      if (scRet = SEC_E_OK)
         break
      if (scRet >= 0x80000000) and (scRet != SEC_I_CONTINUE_NEEDED)
      {
         msgbox InitializeSecurityContext loop error
         exitapp
      }
      if (scRet = SEC_I_INCOMPLETE_CREDENTIALS)
      {
         msgbox SEC_I_INCOMPLETE_CREDENTIALS error
         exitapp
      }
      InBuffers1_BufferType := NumGet(InBuffers, 8+A_PtrSize+4, "uint")
      if (InBuffers1_BufferType = SECBUFFER_EXTRA)
      {
         InBuffers1_cbBuffer := NumGet(InBuffers, 8+A_PtrSize, "uint")
         DllCall("RtlMoveMemory", "ptr", &IoBuffer, "ptr", &IoBuffer + cbIoBuffer - InBuffers1_cbBuffer, "ptr", InBuffers1_cbBuffer)
         cbIoBuffer := InBuffers1_cbBuffer
      }
      else
         cbIoBuffer := 0
   }
}
return

EncryptSend:
pbMessage := &pbIoBuffer+Sizes_cbHeader
cbMessage := StrLen(sendData)
StrPut(sendData, pbMessage, cbMessage, "UTF-8")
VarSetCapacity(Buffers, (8+A_PtrSize)*4, 0)
NumPut(Sizes_cbHeader, Buffers, (8+A_PtrSize)*0, "uint")   ; cbBuffer
NumPut(SECBUFFER_STREAM_HEADER, Buffers, (8+A_PtrSize)*0 + 4, "uint")   ; BufferType
NumPut(&pbIoBuffer, Buffers, (8+A_PtrSize)*0 + 8, "ptr")   ; pbBuffer
NumPut(cbMessage, Buffers, (8+A_PtrSize)*1, "uint")   ; cbBuffer
NumPut(SECBUFFER_DATA, Buffers, (8+A_PtrSize)*1 + 4, "uint")   ; BufferType
NumPut(pbMessage, Buffers, (8+A_PtrSize)*1 + 8, "ptr")   ; pbBuffer
NumPut(Sizes_cbTrailer, Buffers, (8+A_PtrSize)*2, "uint")   ; cbBuffer
NumPut(SECBUFFER_STREAM_TRAILER, Buffers, (8+A_PtrSize)*2 + 4, "uint")   ; BufferType
NumPut(pbMessage + cbMessage, Buffers, (8+A_PtrSize)*2 + 8, "ptr")   ; pbBuffer
VarSetCapacity(Message, 8+A_PtrSize, 0)
NumPut(4, Message, 4, "uint")   ; cBuffers
NumPut(&Buffers, Message, 8, "ptr")   ; pBuffers
scRet := DllCall(NumGet(g_pSSPI+25*A_PtrSize), "ptr", &hContext, "uint", 0, "ptr", &Message, "uint")   ; EncryptMessage
if (scRet != SEC_E_OK)
{
   msgbox EncryptMessage error
   exitapp
}
cbData := DllCall("ws2_32\send", "int", socket, "ptr", &pbIoBuffer, "int", Sizes_cbHeader + cbMessage + Sizes_cbTrailer, "int", 0)
if (cbData <= 0)
{
   msgbox ws2_32\send EncryptMessage error
   exitapp
}

ReadDecrypt:
DecryptedData := ""
cbIoBuffer := scRet := 0
loop
{
   if (cbIoBuffer = 0) or (scRet = SEC_E_INCOMPLETE_MESSAGE)
   {
      cbData := DllCall("ws2_32\recv", "int", socket, "ptr", &pbIoBuffer+cbIoBuffer, "int", cbIoBufferLength-cbIoBuffer, "int", 0)
      if (cbData = -1)
      {
          msgbox SOCKET_ERROR
          exitapp
      }
      else if (cbData = 0)
      {
         if cbIoBuffer
         {
            msgbox SEC_E_INTERNAL_ERROR
            exitapp
         }
         else
            break
      }
      else
         cbIoBuffer += cbData
   }
   VarSetCapacity(Buffers, (8+A_PtrSize)*4, 0)
   NumPut(cbIoBuffer, Buffers, 0, "uint")   ; cbBuffer
   NumPut(SECBUFFER_DATA, Buffers, 4, "uint")   ; BufferType
   NumPut(&pbIoBuffer, Buffers, 8, "ptr")   ; pbBuffer
   VarSetCapacity(Message, 8+A_PtrSize, 0)
   NumPut(4, Message, 4, "uint")   ; cBuffers
   NumPut(&Buffers, Message, 8, "ptr")   ; pBuffers
   scRet := DllCall(NumGet(g_pSSPI+26*A_PtrSize), "ptr", &hContext, "ptr", &Message, "uint", 0, "uint*", 0, "uint")   ; DecryptMessage
   if (scRet = SEC_I_CONTEXT_EXPIRED)
      break
   if (scRet != SEC_E_OK) and (scRet != SEC_I_RENEGOTIATE) and (scRet != SEC_I_CONTEXT_EXPIRED)
   {
      msgbox DecryptMessage error
      exitapp
   }
   pDataBuffer := pExtraBuffer := ""
   loop 4
   {
      Buffers_BufferType := NumGet(Buffers, (A_Index-1)*(8+A_PtrSize) + 4, "uint")
      if (pDataBuffer = "") and (Buffers_BufferType = SECBUFFER_DATA)
         pDataBuffer  := &Buffers + (A_Index-1)*(8+A_PtrSize)
      if (pExtraBuffer = "") and (Buffers_BufferType = SECBUFFER_EXTRA)
         pExtraBuffer  := &Buffers + (A_Index-1)*(8+A_PtrSize)
   }
   if pDataBuffer
   {
      pDataBuffer_cbBuffer := NumGet(pDataBuffer+0, 0, "uint")
      pDataBuffer_pvBuffer := NumGet(pDataBuffer+0, 8, "ptr")
      DecryptedData := strget(pDataBuffer_pvBuffer, pDataBuffer_cbBuffer, "utf-8")
      if (Substr(DecryptedData, -1) = "`r`n")
         break
   }
   if pExtraBuffer
   {
      pExtraBuffer_cbBuffer := NumGet(pExtraBuffer+0, 0, "uint")
      pExtraBuffer_pvBuffer := NumGet(pExtraBuffer+0, 8, "ptr")
      DllCall("RtlMoveMemory", "ptr", &pbIoBuffer, "ptr", pExtraBuffer_pvBuffer, "ptr", pExtraBuffer_cbBuffer)
      cbIoBuffer := pExtraBuffer_cbBuffer
   }
   else
      cbIoBuffer := 0
   if(scRet = SEC_I_RENEGOTIATE)
   {
      cbIoBufferPrev := cbIoBuffer
      fDoRead := 0
      GoSub HandshakeLoop
      InBuffers1_BufferType := NumGet(InBuffers, 8+A_PtrSize+4, "uint")
      if (InBuffers1_BufferType = SECBUFFER_EXTRA)
      {
         InBuffers1_cbBuffer := NumGet(InBuffers, 8+A_PtrSize, "uint")
         DllCall("RtlMoveMemory", "ptr", &pbIoBuffer, "ptr", &IoBuffer + cbIoBuffer - InBuffers1_cbBuffer, "ptr", InBuffers1_cbBuffer)
         cbIoBuffer := InBuffers1_cbBuffer
      }
      else
         cbIoBuffer := cbIoBufferPrev
   }
}
return

base64(login, password)
{
   VarSetCapacity(bin, StrPut(login, "UTF-8") + StrPut(password, "UTF-8"))
   size := StrPut(login, &bin+1, "UTF-8")
   size += StrPut(password, &bin+1+size, "UTF-8")
   DllCall("crypt32\CryptBinaryToString", "ptr", &bin, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "ptr", 0, "uint*", chars)
   VarSetCapacity(outData, chars << !!A_IsUnicode, 0)
   DllCall("crypt32\CryptBinaryToString", "ptr", &bin, "uint", size, "uint", (CRYPT_STRING_BASE64 := 0x1)|(CRYPT_STRING_NOCRLF := 0x40000000), "str", outData, "uint*", chars)
   return outData
}

Тема для обсуждения