rlebeau on master
February-29th-workaround (#338)… (compare)
your comment makes sense in case if it hangs while calling Readable (ATimeout) in TIdIOHandler.ReadFromSource. But in reality it hangs inside call
LByteCount := ReadDataFromSource(LBuffer); a few lines below. I can imagine next multithread scenario:
1) first thread actively does IO over socket
2) second thread checks for connection and for this moment due activity of first thread Readable returns true.
3) Second thread calls ReadDataFromSource, but for this moment first thread fully has processed data => second thread hangs there.
Remobject's SuperTCPServer depends on Indy library and assumes that Indy10 supports multithread processing.
As for me fix should be in calling of just Readable inside TIdIOHandlerStack.Connected instead of ReadFromSource
@josdebr_twitter what is the EXACT error message? What does the call stack look like when the error occurs?
the exact error message is: "ERangeError raised: Range check error". I get the error after I call "IMAPClient.Retrieve(i, Msg)"
I've managed to generate a callstack, I can even get you a Eureka log file if you can read it. But here is the callstack extracted from my log:
IdGlobalProtocols.pas| |GetUniqueFileName |1644 |
IdGlobalProtocols.pas| |GetUniqueFileName |1599 |
IdGlobalProtocols.pas| |MakeTempFilename |1584 |
IdGlobalProtocols.pas| |MakeTempFilename |1549 |
IdAttachmentFile.pas |TIdAttachmentFile |PrepareTempStream |144 |
IdMessageClient.pas | |ProcessAttachment |840 |
IdMessageClient.pas | |ProcessAttachment |833 |
IdMessageClient.pas |TIdMessageClient |ReceiveBody |1024|
IdMessageClient.pas |TIdMessageClient |ProcessMessage |1535 |
IdMessageClient.pas |TIdMessageClient |ProcessMessage |1527 |
IdMessageHelper.pas | |Internal_TIdMessageClientHelper_ProcessMessage|60 |
IdMessageHelper.pas | |Internal_TIdMessageClientHelper_ProcessMessage|48 |
IdMessageHelper.pas | |Internal_TIdMessageHelper_LoadFromStream |85 |
IdMessageHelper.pas | |Internal_TIdMessageHelper_LoadFromStream |76 |
IdMessageHelper.pas | |TIdMessageHelper_LoadFromStream |97 |
IdMessageHelper.pas | |TIdMessageHelper_LoadFromStream |96 |
IdIMAP4.pas |TIdIMAP4 |InternalRetrieve |4951 |
IdIMAP4.pas |TIdIMAP4 |InternalRetrieve |4892 |
IdIMAP4.pas |TIdIMAP4 |Retrieve |4601 |
IdIMAP4.pas |TIdIMAP4 |Retrieve |4599 |
@klsyzzz OpenSSL certificate validation with Indy is a little bit tricky (or maybe its the OpenSSL part that makes it tricky^^), sslvrfPeer says "if the server sends a certificate, it wil be verified. If the verification fails, the handshake will be terminated immediately. The only time that a server would not send a certificate is when an anonymous cipher is in use" (which should never be used^^). sslvrfFailIfNoPeerCert and sslvrfClientOnce are only used for server. If no VerifyMode is set, OpenSSL (in client mode) will verify the server certificate, but failure will not terminate the handshake.
I even believe you have always to implement a OnVerifyPeer, otherwise Indy uses a default implementation which is
Result := True which overrides the verification result of OpenSSL.
If OpenSSL should verify the certificate, it needs to know which certificate are trustable. For this OpenSSL uses all public certs which are stored in the directory which you specified in SSLOptions.VerifyDir. If you want to use the windows certificate store, you could use this snippet, but you have to have a look into the msdn for
CertEnumCertificatesInStore and maybe is
CertEnumSystemStore interessting for you.
LCert := CertEnumCertificatesInStore(LStore, nil); while Assigned(LCert) do begin LX509 := d2i_X509(nil, @lCert.pbCertEncoded, LCert.cbCertEncoded); if Assigned(LX509) then begin X509_STORE_add_cert(ctx.cert_store, LX509); X509_free(LX509); end; LCert := CertEnumCertificatesInStore(LStore, LCert); end; // Calls of CertFreeCertificateContext are not needed, because // CertEnumCertificatesInStore frees the pPrevCertContext Argument
Another Point for certificate verification is the VerifyDepth: OpenSSL uses as default 9, but Indy overrides this with its own default, which is default(Integer) = 0. If the SMTP Server sends a certificate issued by a intermediate CA, OpenSSL terminate the connection with cert chain too long. VerifyDepth specifies the max length of a certificate chain.
@rlebeau I've changed the function to use a UID for the temp file name:
if(CreateGuid(Uid) = S_OK) then Result := Copy(stringreplace(GuidToString(Uid),'-','', [rfReplaceAll]), 2, 10) + LFQE; // Result := LFName + IntToHex(LNamePart, 8) + LFQE; if not FileExists(Result) then begin Break; end;
The first test of this change is hopefull, my application is again able to parse the messages with attachments.
Can you think of any problems I might run into using this change (Apart from compatibilty issues when I try to upgrade the Indy components)