I have a TIdPop3Server in one application that has a IdServerIOHandlerSSLOpenSSL1 attached to it and retrieves emails and sends them to a TIdPop3 client in another application (having TIdSSLIOHandlerSocketOpenSSL attached to it). Everything's fine when the connections are made insecure using port 110. But when I try to use SSL connection through port 995 I get error Connection Closed Gracefully after connect attemp from the client fails. This is my Pop3SeverOnConnect event :
procedure TMainForm.Pop3ServerConnect(AContext: TIdContext);
begin
if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough :=
(AContext.Binding.Port <> 995);
showmessage('SSL connection made!');
end;
And this is the client-side :
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
IdSSLIOHandlerSocketOpenSSL1.PassThrough := False;
POP3Client.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
with POP3Client do begin
AuthType := patUserPass;
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
Except on e : Exception do
showmessage('error=' + e.Message);
end;
// code for retrieving message data
end;
And I always get an exception from Pop3Client.Connect like I've already mentioned above (The message SSL connection made! in the server application never shows up). If I use however another mail client like for example Mozilla Thunderbird I achieve a successful SSL connection for port 995. So the problem should be somewhere in the client's procedure but who knows - that's why I'm asking you guys for help.
In your client code, you need to set the TIdPOP3.UseTLS property instead of the TIdSSLIOHandlerSocketOpenSSL.PassThrough property directly, eg:
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
with POP3Client do
begin
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AuthType := patUserPass;
UseTLS := utUseImplicitTLS; // <-- here
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
try
// code for retrieving message data
finally
POP3Client.Disconnect;
end;
except
on e : Exception do
ShowMessage('error=' + e.Message);
end;
end;
In your server code, you need to get rid of the ShowMessage(). TIdPOP3Server is multi-threaded, the OnConnect event is fired in the context of a worker thread, and ShowMessage() is not thread-safe. If you must display a popup message, use Windows.MessageBox() instead.
Related
On the below piece of code i have some related questions
var
UseProxy : Boolean = True;
....
var
IdUDPClient : TIdUDPClient;
sText : string;
begin
IdUDPClient := TIdUDPClient.Create(nil);
try
IdUDPClient.Host := '10.10.10.10';
IdUDPClient.Port := 5555;
if UseProxy then begin
IdUDPClient.TransparentProxy.Enabled := True;
IdUDPClient.TransparentProxy.Host := '20.20.20.20';
IdUDPClient.TransparentProxy.Port := 8080;
IdUDPClient.OpenProxy;
end;
try
IdUDPClient.Connect;
except
Writeln('Connect Error.');
end;
if IdUDPClient.Connected then
Writeln('Connected')
else begin
Writeln('Not Connected');
Exit;
end;
try
IdUDPClient.Send('Foo');
try
sText := IdUDPClient.ReceiveString(1000);
Writeln('Received: ', sText);
except
Writeln('Receive Error.');
end;
except
Writeln('Send Error.');
end;
if UseProxy then
IdUDPClient.CloseProxy;
finally
IdUDPClient.Free;
end;
Readln;
end.
Why the try...except block is not catching errors on the UDP
client even if the Host is not reachable nor the port is closed e.g. IdUDPClient.Connected always True?
When using the proxy i'm not sure if my implementation is correct above
because if UseProxy is True the IdUDPClient try to directly send
the request to 10.10.10.10not thru the proxy server. How i can fix this? what i'm doing wrong?
My test scenario for the proxy was as follow:
My PC IP 30.30.30.30
UDP Server 10.10.10.10
Proxy Sever (Socks 5) 20.20.20.20
30.30.30.0/24 cant reach 10.10.10.10 but 20.20.20.20 does.
if my PC can access the UDP server directly and i put fake proxy server (unused IP with random port) to the client. the client can reach the UDP server which is not supposed to be reached because the proxy is down. How i cant prevent such scenario ?
UDP is connection-less, so Connected cannot tell you whether the remote peer is reachable or not.
"Connecting" a UDP socket simply assigns a local association with the peer IP/Port. That way, outbound packets are sent only to that peer, and only packets received from that peer are accepted. Nothing more. There is no actual connection, like in TCP.
And unlike TCP, sending a UDP packet just dumps the packet onto the network, there is no acknowledgement that the packet ever reaches the peer. Acks have to be implemented at the application layer.
As for exceptions, they are only raised when an actual error occurs. In UDP, the only way an unreachable Host would cause socket errors is if the network sends back an ICMP packet to indicate the Host is not reachable. A "connected" UDP socket will receive such packets internally and start reporting failures on subsequent reads/sends with the same peer. So, in your example, the call to Send() will never raise an error about not being about to reach the Host, because it simply does not know. ReceiveString() has a better chance of reporting such errors, however the way it is currently implemented, it would likely just ignore them because it checks if the socket is readable (has a pending UDP packet) before actually reading it. A ICMP packet may not make the socket enter a readable state.
Since you are specifying a timeout on ReceiveString(), you will just have to assume the peer is gone if the timeout elapses without receiving an actual string.
Your code is bypassing the proxy because you are not setting up the TransparentProxy correctly.
When you access the TransparentProxy property, if no proxy component is currently assigned then the property getter creates an internal TIdSocksInfo object. TIdSocksInfo has a Version property that defaults to svNoSocks. Since you are trying to connect through a SOCKS v5 proxy, you need to set the Version to svSocks5 instead:
if UseProxy then begin
(IdUDPClient.TransparentProxy as TIdSocksInfo).Version := svSocks5; // <--
IdUDPClient.TransparentProxy.Host := '20.20.20.20';
IdUDPClient.TransparentProxy.Port := 8080;
end;
The TransparentProxy.Enabled property setter is not used with TIdSocksInfo at all, it is a no-op. However, the Enabled property getter returns True/False depending on the value of the TIdSocksInfo.Version property.
And you don't need to call OpenProxy() and CloseProxy() manually, TIdUDPClient.Connect() and TIdUDPClient.Disconnect() will handle that for you when the TransparentProxy is enabled.
Now, with all of that said, try this:
var
UseProxy : Boolean = True;
...
var
IdUDPClient : TIdUDPClient;
IdSocksInfo : TIdSocksInfo;
sText : string;
begin
IdUDPClient := TIdUDPClient.Create(nil);
try
IdUDPClient.Host := '10.10.10.10';
IdUDPClient.Port := 5555;
if UseProxy then begin
IdSocksInfo := TIdSocksInfo.Create(IdUDPClient);
IdSocksInfo.Version := svSocks5;
IdSocksInfo.Host := '20.20.20.20';
IdSocksInfo.Port := 8080;
IdUDPClient.TransparentProxy := IdSocksInfo;
end;
try
IdUDPClient.Connect;
except
on E: Exception do begin
Writeln('Connect Error: ', E.Message);
Exit;
end;
end;
try
Writeln('Connected.');
try
IdUDPClient.Send('Foo');
except
on E: Exception do begin
Writeln('Send Error: ', E.Message);
Exit;
end;
end;
try
sText := IdUDPClient.ReceiveString(1000);
if sText <> '' then
Writeln('Received: ', sText)
else
Writeln('Nothing Received after 1 second.');
except
on E: Exception do begin
Writeln('Receive Error: ', E.Message);
end;
end;
finally
IdUDPClient.Disconnect;
end;
finally
IdUDPClient.Free;
end;
end.
I tried with Indy component to send email in XE2, and it works fine on my laptop that I compiled my project on.
But if I take my exe project to another PC, it shows an error message
Connection closed gracefully
or, sometimes I get
SSL Negotiation failed
Actually I tried many times to solve my problem, but I can't.
This is my code - where is my mistake? I need a practical solution.
procedure Gmail(username, password, totarget, subject, body :string);
var
DATA : TIdMessage;
SMTP : TIdSMTP;
SSL : TIdSSLIOHandlerSocketOpenSSL;
result:Boolean;
begin
try
SMTP := TIdSMTP.Create(nil);
DATA := TIdMessage.Create(nil);
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SSL.Destination := 'smtp.gmail.com:587';
SSL.Host := 'smtp.gmail.com';
// SSL.MaxLineAction.maException;
SSL.Port:= 587;
SSL.SSLOptions.Method := sslvTLSv1;
SSL.SSLOptions.Mode := sslmUnassigned;
SSL.SSLOptions.VerifyMode := [];
SSL.SSLOptions.VerifyDepth := 0;
DATA.From.Address := username;
DATA.Recipients.EMailAddresses := totarget;
DATA.Subject := subject;
DATA.Body.Text := body;
if FileExists('D:\Test1.txt') then
TIdAttachmentFile.Create(DATA.MessageParts, 'D:\Test1.txt');
SMTP.IOHandler := SSL;
SMTP.Host := 'smtp.live.com';
SMTP.Port := 587 ;
SMTP.Username := username;
SMTP.Password := password;
// SMTP.SASLMechanisms;
SMTP.UseTLS := utUseExplicitTLS;
try
try
SMTP.Connect;
SMTP.Send(DATA);
Result := True;
except
on E:Exception do
begin
ShowMessage('Cannot send E-Mail: ' + E.Message);
Result := False;
end;
end;
finally
if SMTP.Connected then SMTP.Disconnect;
end;
except
on E : Exception do
begin
ShowMessage('Error in the function SendEmailDelphi: ' + E.Message);
Result := False;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled:= True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
mail_username := 'email#gmail.com';
mail_password := 'pass';
mail_to := 'email#gmail.com';
mail_subject := 'Text email from Delphi project';
mail_body := 'this is a test email sent from Delphi project, do not reply';
try
begin
Gmail(mail_username, mail_password, mail_to , mail_subject, mail_body);
end;
finally
end;
end;
DO NOT set the SSL.Destination, SSL.Host, or SSL.Port properties manually! TIdTCPClient.Connect() handles that for you. Besides, don't you think it's odd that you are setting SSL.Destination/Host to smtp.gmail.com but are setting SMTP.Host to smtp.live.com instead? Gmail and Live are not the same service provider.
Also, SSL.SSLOptions.Mode should be set to sslmClient instead of sslmUnassigned. Not too important, TIdSSLIOHandlerSocketOpenSSL will simply flip it when it configures the connection. But you should do it anyway, since you know your code is acting as a client.
And lastly, try setting SMTP.UseTLS before setting SMTP.Port, as setting UseTLS may change the Port, so you want to make sure you are really connecting to the correct port you are expecting.
With that said, the SSL Negotiation failed error means the TLS handshake was started but failed part-way through the negotiation. Try assigning handlers to TIdSSLIOHandlerSocketOpenSSL's OnStatusInfo/Ex events to see how far the handshake is actually getting. And if you are using a relatively modern version of Indy 10, try looking at the raised exception's InnerException property, it might give you a clue as to what went wrong before the EIdTLSClientTLSHandShakeFailed exception was raised afterwards.
I am testing IndyFTP to upload a file to a server. The file is uploaded but has 0 bytes because there is a EIdAccessTimeout exception - 'Accept timed out". How can I prevent the exception? Is my code incorrect? The code is shown below:
procedure TForm1.FTPUpload1Click(Sender: TObject);
{ Indy FTP Upload. }
var
iHost: string;
iUsername: string;
iPassword: string;
iFolder: string;
iSourceFile: string;
iDestFile: string;
iAppend: boolean;
iStartPos: Int64;
begin
iHost := FTPHost1.Text;
iUsername := Username1.Text;
iPassword := Password1.Text;
iFolder := ServerFolder1.Text;
if FileExists(SourceFile1.Text) then
iSourceFile := SourceFile1.Text
else
Exit;
if FileExists(SourceFile1.Text) then
iDestFile := ExtractFileName(SourceFile1.Text)
else
Exit;
iAppend := False;
iStartPos := -1;
IdFTP1.Host := iHost;
IdFTP1.Username := iUsername;
IdFTP1.Password := iPassword;
IdFTP1.Connect;
IdFTP1.TransferType := ftBinary;
IdFTP1.Put(iSourceFile);
IdFTP1.Disconnect;
end;
There are some unused vars listed because I am just learning and have not used some of the parameters yet.
Most likely, your FTP client is set to ACTIVE mode, so this error means that after a successful login to the FTP server, the "reverse" connection couldn't be established (the file transfer).
In active mode FTP the client connects from a random unprivileged port
(N > 1023) to the FTP server's command port, port 21. Then, the client
starts listening to port N+1 and sends the FTP command PORT N+1 to the
FTP server. The server will then connect back to the client's
specified data port from its local data port, which is port 20.
Active FTP vs. Passive FTP, a Definitive Explanation
You can set to passive mode this way:
IdFTP1.Passive := True;
EDIT
In addition, use try-except-finally blocks, so you can do some error handling. Something like:
try
IdFTP1.Connect;
try
IdFTP1.Put(...);
finally
IdFTP1.Disconnect;
end;
except
// couldn't connect
end;
I want to establish a communication between TIdTCPServer and TIdTCPClient in delphi and this is how my procedures look :
1.Server side :
procedure TMainForm.IdTCPServer1Execute(AContext: TIdContext);
var
clientReq, clientName : String;
begin
clientReq := AContext.Connection.IOHandler.ReadLn(); // client sends request
clientName := extractClientName(clientReq);
AContext.Connection.IOHandler.WriteLn('Hello ' + clientName);
end;
2.Client side :
procedure TMainForm.btnTestClientClick(Sender: TObject);
var
testTCP : TIdTCPClient;
clientReq, serverResponse : String;
begin
testTCP := TIdTCPClient.Create;
try
testTCP.Host := wantedHost;
testTCP.Port := wantedPort;
testTCP.Connect;
clientReq := 'Hello, my Name is user1.';
testTCP.IOHandler.WriteLn(clientReq);
try
serverResponse := testTCP.IOHandler.ReadLn();
except on e : Exception do begin
ShowMessage('Error reading response =' + e.Message);
end;
end;
finally
FreeAndNil(testTCP);
end;
end;
I connect to the server but than my application freezes when I try to receive the response from the server OnExecute event with my TCPClient.IOHandler.ReadLn method. Can anyone help me fix my code or show me a working example of what I'm trying to do (with Indy's TIdTCPClient and TIdTCPServer) ?
There is nothing wrong with the code you have shown, so the problem has to be in the code you have not shown. The way I see it, there are two possibilities:
If you are not setting wantedHost and/or wantedPort to the correct values, you would not actually be connecting to your expected server.
If extractClientName() is getting stuck internally and not exiting, the server would not be sending any response. One way that could happen is if you are running the client and server in the same process, and extractClientName() syncs with the main thread, but the main thread is blocked waiting on the client and cannot process the sync, so a deadlock occurs.
Using Delphi XE2, Indy 10 that ships with it and OpenSSL dll's version 1.0.1c.
We are in the final stages of converting our Delphi 6 project to Delphi XE2.
We have solved every show stopper so far but I am running into one trying to connect to Gmail smtp server.
Our Delphi 6 app works fine using SSL v2.3 and SSL v3 and Indy 9 components connecting to port 465 per Google's own documentation. Google Support
Our Delphi XE2 app does not connect at all. The call to connect goes into the ether and nothing happens for some 7 minutes until I get bored waiting for the thing to timeout and kill it off. I actually traced the program execution all the way to the IdWinsock2 funcion here:
function Stub_select(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Integer; stdcall;
begin
#select := FixupStub(hWinSockDll, 'select'); {Do not Localize}
Result := select(nfds, readfds, writefds, exceptfds, timeout);//<-this line never returns and timeout is always nil
end;
I need to maintain the old SSL connectivity for existing users' configurations.
New users will be able to connect using TLS (to port 587) and that DOES work!?!?
I am at a loss as to why the non-TLS SSL options do not work and do not report any error and do not time out but just go off to Never-Never Land. Please help!
TJ
Here is the code:
SMTPClient.Host := trim(EditSMTPServer.Text);
SMTPClient.Port := EditSMTPServerPort.AsInteger;//<- value from custom edit box
SMTPClient.Username := trim(EditSMTPLogon.Text);
SMTPClient.Password := trim(EditSMTPPassword.Text);
SMTPClient.AuthType := satDefault;
if CheckBoxUseSSL.Checked then begin
SMTPClient.IOHandler := IdSSLIOHandlerSocket1;
IdSSLIOHandlerSocket1.ReadTimeout := 30000;//30 second timeout which does not appear to work
case ComboBoxSSLMode.ItemIndex of
0 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv2;
1 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv23;
2 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv3;
3 : begin
IdSSLIOHandlerSocket1.SSLOptions.Method := sslvTLsv1;
SMTPClient.UseTLS := utUseImplicitTLS;
end;
end;//case
IdSSLIOHandlerSocket2.SSLOptions.Method := IdSSLIOHandlerSocket1.SSLOptions.Method;
IdSSLIOHandlerSocket2.PassThrough := False;
end
else begin
SMTPClient.IOHandler := nil;
end;
try
SMTPClient.Connect;
if SMTPClient.Connected then begin
if SMTPClient.Authenticate then begin
... do some work ...
end;
end;
except
on e:Exception do begin
showmessage(e.message);
end;
end;
EDIT:
As usual, after I post a question I have stumbled across a workaround.
If I set the UsetTLS property to utUseImplicitTLS for all NON-SSL transactions and set it to utUseExplicitTLS for TLS transactions my connections appear to work in a timely manner.
Hopefully this helps someone out.
updated code:
if CheckBoxUseSSL.Checked then begin
SMTPClient.IOHandler := IdSSLIOHandlerSocket1;
SMTPClient.UseTLS := utUseImplicitTLS;
case ComboBoxSSLMode.ItemIndex of
0 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv2;
1 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv23;
2 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv3;
3 : begin
IdSSLIOHandlerSocket1.SSLOptions.Method := sslvTLsv1;
SMTPClient.UseTLS := utUseExplicitTLS;
end;
end;//case
IdSSLIOHandlerSocket2.SSLOptions.Method := IdSSLIOHandlerSocket1.SSLOptions.Method;
IdSSLIOHandlerSocket2.PassThrough := False;
end
else begin
SMTPClient.UseTLS := utNoTLSSupport;//reset TLS support flag
SMTPClient.IOHandler := nil;
end;
What you discovered is what you are supposed to be doing. SSL and TLS have different semantics, so you have to set UseTLS accordingly.
For SSL on port 465, the server expects your client to initiate an SSL handshake immediately upon connecting, before the server then sends an encrypted Greeting. UseTLS=utUseImplicitTLS does that, but UseTLS=utUseExplicitTLS does not. When UseTLS=utUseExplicitTLS, TIdSMTP will expect the server to send an unencrypted Greeting immediately, which would explain the hang in select() - the server is not sending any data!
For TLS on port 587, the server is expecting the client to connect initially unencrypted and then send an explicit STARTTLS command when it is ready to initiate encryption. UseTLS=utUseExplicitTLS does that, but UseTLS=utUseImplicitTLS does not.