How can i resolve dns with TIdDNSResolver? - delphi

when try to resolve dns i am getting query refused by dns error error . am i wrong with that code can you please check ?
function Lookup(const Name: String): String;
var
X: Integer;
DNS: TIdDNSResolver;
begin
DNS:= TIdDNSResolver.Create(nil);
try
DNS.Host := 'mail.relativ.art';
DNS.WaitingTime := 5000;
DNS.QueryType:= [qtMX];
try DNS.Resolve(Name); except on E: Exception do begin writeln(E.Message); end; end;
for X:= 0 to DNS.QueryResult.Count-1 do begin
if DNS.QueryResult[X].RecType = qtMX then
Result:= TMXRecord(DNS.QueryResult[X]).ExchangeServer;
end;
finally
DNS.Free;
end;
end;

A hostname with mail in its name, like mail.relativ.art, is likely not a DNS server.
You need to set the Host property to an actual DNS server (preferrably one provided by your ISP, or a public one like Google), and then pass the desired target name to Resolve().
If you are trying to query MX records for mail.relativ.art, then that is the target host to resolve, not the DNS server to send the query to, eg:
DNS:= TIdDNSResolver.Create(nil);
try
DNS.Host := '8.8.8.8'; // Google public DNS
DNS.WaitingTime := 5000;
DNS.QueryType := [qtMX];
try
DNS.Resolve('mail.relativ.art');
except
on E: Exception do
begin
WriteLn(E.Message);
end;
end;
for X:= 0 to DNS.QueryResult.Count-1 do
begin
if DNS.QueryResult[X].RecType = qtMX then
Result := TMXRecord(DNS.QueryResult[X]).ExchangeServer;
end;
finally
DNS.Free;
end;

Related

Delphi UDP Hole Punching: on internet not always works

I'm trying to implement UDP Hole Punching with Delphi with Indy and Firemonkey technology.
I have tried to follow this document: https://www.researchgate.net/publication/1959162_Peer-to-Peer_Communication_Across_Network_Address_Translators
The program seems to work but is NOT stable.
If I work on a system on the local intranet no problem.
If I work on an internet, it doesn't always work and I don't know why.
I have created two applications.
The first is server side.
Everytime all clients connect correctly to server.
The server registers the Local IP and Internet IP pairs in a variable (fPeers).
I created an IdUDPServer instance.
This is the “Connect push button” code:
procedure TForm1.B_ConnectClick(Sender: TObject);
var
vIdSocketHandle: TIdSocketHandle;
begin
if IdUDPServer.Active then
begin
IdUDPServer.Active := False;
B_Connect.Text := 'Connect';
end
else
begin
IdUDPServer.Bindings.Clear;
vIdSocketHandle := IdUDPServer.Bindings.Add;
vIdSocketHandle.IP := GStack.LocalAddress;
vIdSocketHandle.Port := E_POrt.Text.ToInteger;
IdUDPServer.Active := True;
B_Connect.Text := 'Disconnect';
end;
end;
During the IdUDPServerUDPRead event I capture the Local and Internet IP addresses of the clients that connect.
In the TStringLIST called fPeerIP I add the list of addresses.
procedure TForm1.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;
const AData: TIdBytes; ABinding: TIdSocketHandle);
var vPair: string;
vData: string;
vString: string;
vLog: string;
begin
vPair := ABinding.PeerIP + ':'+ABinding.PeerPort.ToString;
vData := BytesToString(AData);
vLog := '';
if leftstr(vdata,7) = 'LOCALIP' then
begin
vString := vPair+#9+lsExtract(vData,2,',');
if fPeerIP.IndexOfName(vString) = -1 then
begin
fPeerIP.Add(vString);
M_Peers.Lines.Add(vString);
vLog := vLog + vString + #13#10;
IdUDPServer.Send(ABinding.PeerIP, ABinding.PeerPort, 'Peer aggiunto alla lista');
end;
end
else vLog := vData;
end;
On the client side, I created an IdUDPServer instance which, upon connection, sends a string to the server.
procedure TForm2.B_ConnectClick(Sender: TObject);
var vIdSocketHandle: TIdSocketHandle;
vLocalAddressList: TIdStackLocalAddressList;
vI: Integer;
vSendLIST: TStringLIST;
begin
if IdUDPServer.Active then
begin
Timer.Enabled := False;
IdUDPServer.Active := False;
B_Connect.Text := 'Connect';
M_Networks.Lines.Clear;
M_Debug.Lines.Clear;
LB_Peers.Items.Clear;
end
else
begin
try
vSendLIST := TStringLIST.Create;
IdUDPServer.Bindings.Clear;
vLocalAddressList := TIdStackLocalAddressList.Create;
GStack.GetLocalAddressList(vLocalAddressList);
M_Networks.Lines.Clear;
for vI := 0 to vLocalAddressList.Count-1 do
begin
if vLocalAddressList.Addresses[vI].IPVersion = id_IPV4 then
begin
M_Networks.Lines.Add(vLocalAddressList.Addresses[vI].IPAddress);
vSendLIST.Add(Format('LOCALIP,%s:%d',[vLocalAddressList.Addresses[vI].IPAddress,E_ClientPort.Text.ToInteger]));
end;
end;
vIdSocketHandle := IdUDPServer.Bindings.Add;
vIdSocketHandle.Port := E_ClientPort.Text.ToInteger;
vIdSocketHandle.IP := '0.0.0.0';
IdUDPServer.Active := True;
for vI := 0 to vSendLIST.Count-1 do
IdUDPServer.Send(E_Server.Text, E_Port.Text.ToInteger, vSendLIST[vI]);
B_Connect.Text := 'Disconnect';
if Assigned(vSendLIST) then FreeAndNil(vSendLIST);
finally
if Assigned(vLocalAddressList) then FreeAndnil(vLocalAddressList);
end;
end;
end;
Also on the client side, in the IdUDPServerUDPRead event I detect the list of Peers (function sent by the server) and send a "PING" to each connected peer.
I realize maybe I have given little information.
I'd like to know your opinion and possibly indicate to me if I made a mistake in the process that activates the Hole Punching.
Thanks in advance
LS
Your code is theoretically right and may work on some NAT routers but it will not work on the rest
I have been trying to achieve UDP Hole Punching for many years but it's really complicated,
you need to combine many NAT Traversal mechanisms together to make it work in the most cases
Reading about STUN, TURN and ICE mechanisms may help

Send email using indy component delphi xe2 SSL negotiation faild

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.

Sending mail using Indy and smtp

Im trying to send mail using this code:
With IdMessage1 Do Begin
Recipients.EMailAddresses := 'XXXXX#gmail.com';
From.Address := 'XXXXX#gmail.com';
From.Name := edit_from.Text;
CCList.EMailAddresses := '';
BccList.EMailAddresses := '';
Priority := mpNormal;
Subject := edit_subject.Text;
Body.Add(memo_body.Lines.Text);
End;
With IdSMTP1 Do Begin
Host := 'smtp.gmail.com';
Username := 'XXXXX#gmail.com';
Password := '*****';
IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
Port := 465;
UseTLS := utUseImplicitTLS;
Try
Connect;
Except
End;
If Not Connected Then Begin
Showmessage('Error');
Exit;
End;
Try
Send(IdMessage1);
Finally
Disconnect;
End;
End;
It works fine on my computer but when i test it on other machines the 'ERROR' (Error in If block before last Try block) will be raised...
Where is the problem?
This is not the proper way to do error handling with Indy. It should be more like this instead:
With IdSMTP1 Do Begin
IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
UseTLS := utUseImplicitTLS;
Host := 'smtp.gmail.com';
Username := 'XXXXX#gmail.com';
Password := '*****';
Port := 465;
Try
Connect;
Try
Send(IdMessage1);
Finally
Disconnect;
End;
Except
Showmessage('Error');
Exit;
End;
End;
Send() and Disconnect() can fail just as easily as Connect() can. If you want Connect() to be in its own try/except block, then at least don't use Connected to validate whether Connect() succeeded:
Try
Connect;
Except
Showmessage('Error connecting');
Exit;
End;
Try
Try
Send(IdMessage1);
Finally
Disconnect;
End;
Except
Showmessage('Error sending');
Exit;
End;
That being said, the exception tells you what actually failed, so do not ignore it. Had you displayed its content, you would have had a better idea of what was failing, eg:
Except
on E: Exception do
Begin
ShowMessage(Format('Error!'#10'[%s] %s', [E.ClassName, e.Message]));
Exit;
End;
End;
The most likely culprit is that you did not deploy the OpenSSL DLLs with your app. You can download them from OpenSSL's website, or from Indy's Fulgan mirror.

delphi no address specified after grabbing ip from dns lookup

Ok, I am getting very frustrated with this, and want to ask for your help before I throw my computer out my window. I am messing with sockets, and I wrote piece of code to get the ip from DNS that I provide. If I do the command showmessage(getipfromdns('test.no-ip.org'));
it will show the ip address no problem.
However if I try client1.Host/address(I have tried both)
client1.Host := getipfromdns('test.no-ip.org');
and throws me an error saying no address specified.
here is the function to get the ip from dns
function getipfromdns(HostName: string): string;
type
tAddr = array [0..100] of PInAddr;
pAddr = ^tAddr;
var
I: Integer;
WSA: TWSAData;
PHE: PHostEnt;
P: pAddr;
begin
Result := HostName;
WSAStartup($101, WSA);
try
PHE := GetHostByName(pChar(HostName));
if (PHE <> nil) then
begin
P := pAddr(PHE^.h_addr_list);
I := 0;
while (P^[i] <> nil) do
begin
Result := (inet_nToa(P^[i]^));
Inc(I);
end;
end;
except
end;
WSACleanup;
end;
and here is the code for when the form is created.
procedure TForm1.FormCreate(Sender: TObject);
var
duchost : string;
begin
duchost := getipfromdns('test.no-ip.org');
Client1.Address := duchost;
Client1.Active := True;
end;
(client1 is type TCPServerSocket)
gethostbyname() can potentially return multiple addresses for a given hostname. You are looping through the entire list but only returning the last IP in the list. That is the wrong thing to do. You need to loop through the list, attempting to connect to each IP one at a time until one succeeds or the list is exhausted.

How to check if a TCP port is available with Delphi?

Is there a more elegant way of checking if a TCP port is available with Delphi other than catching a netstat call?
I guess you can use Indy's components to do that. For instance a TIdHTTPServer will raise an exception if a port is in use when it is being opened.
So basically you could create such component, bind it to localhost:<yourport> and if an exception is raised ( catch it and check it ) then the port is probably in use, else it is free.
I guess other indy components can tell if a port is open or not, but I can't look at it right now.
This was just to give you an approach.
#Mattl, if Available means open for you, you can use this code.
program CheckTCP_PortOpen;
{$APPTYPE CONSOLE}
uses
Winsock; //Windows Sockets API Unit
function PortTCPIsOpen(dwPort : Word; ipAddressStr:string) : boolean;
var
client : sockaddr_in;//sockaddr_in is used by Windows Sockets to specify a local or remote endpoint address
sock : Integer;
begin
client.sin_family := AF_INET;
client.sin_port := htons(dwPort);//htons converts a u_short from host to TCP/IP network byte order.
client.sin_addr.s_addr := inet_addr(PChar(ipAddressStr)); //the inet_addr function converts a string containing an IPv4 dotted-decimal address into a proper address for the IN_ADDR structure.
sock :=socket(AF_INET, SOCK_STREAM, 0);//The socket function creates a socket
Result:=connect(sock,client,SizeOf(client))=0;//establishes a connection to a specified socket.
end;
var
ret : Integer;
wsdata : WSAData;
begin
Writeln('Init WinSock');
ret := WSAStartup($0002, wsdata);//initiates use of the Winsock
if ret<>0 then exit;
try
Writeln('Description : '+wsData.szDescription);
Writeln('Status : '+wsData.szSystemStatus);
if PortTCPIsOpen(80,'127.0.0.1') then
Writeln('Open')
else
Writeln('Close');
finally
WSACleanup; //terminates use of the Winsock
end;
Readln;
end.
netstat information can be retrieved by calling the GetTcpTable and GetUdpTable functions in the IP Helper API, or IPHLPAPI.DLL. For more information on calling the IPHLPAPI.DLL from Delphi, check out this Network traffic monitor. There are some wrappers for it too, and it is part of JEDI API Library.
I wrote a Delphi version of NetStat long ago, but have since lost the source code. Those resources should get you started though.
The following code from Synapse works very well:
uses
blcksock;
function PortAvailable(Port:STring):boolean;
var
svr : TTCPBlockSocket;
begin
svr := TTCPBlockSocket.Create;
try
svr.Bind('0.0.0.0',Port);
svr.Listen;
result := svr.LastError = 0;
Svr.CloseSocket;
finally
svr.Free;
end;
end;
Using an Indy.Sockets v10 TIdTCPServer component:
function TExample.IsTCPPortAvailable(const APort: Word): Boolean;
var
LTCPServer: TIdTCPServer;
LBinding: TIdSocketHandle;
begin
Result := True;
LTCPServer := TIdTCPServer.Create;
try
try
with LTCPServer do
begin
DefaultPort := APort;
LBinding := Bindings.Add;
LBinding.IP := '127.0.0.1';
LBinding.Port := APort;
OnExecute := TCPServerExecute;
Active := True;
end;
finally
LTCPServer.Free;
end;
except on EIdCouldNotBindSocket do
Result := False;
end;
end;
procedure TExample.TCPServerExecute(AContext: TIdContext);
begin
end;
Based on Silver's example above, and since in many cases you want to find an available port rather than just verifying that a given port is in use:
uses
//Indy V10
IdContext,
IdSocketHandle,
IdTcpServer;
type
//our port-checking tool
TPortChk = class(TIdTCPServer)
procedure OnExec(AContext: TIdContext);
end;
procedure TPortChk.OnExec(AContext: TIdContext);
begin
//does nothing, but must exist and be hooked
end;
//check a TCP port to see if it's already in use.
//normally used before opening a listener.
function PortAvailable(APort: Word): Boolean;
var
svr: TPortChk;
bnd: TIdSocketHandle;
begin
//assume our port is available
Result := True;
//create our checking object
svr := TPortChk.Create;
try
//set the execute event
svr.OnExecute := svr.OnExec;
//loop looking for an available port
try
//set up the binding for our local system and the
//port in question
bnd := svr.Bindings.Add;
bnd.IP := '127.0.0.1';
bnd.Port := APort;
//try to bind. This will throw an EIdCouldNotBindSocket
//exception if the port is already in use.
svr.Active := True;
//if we get here, the port is *currently* available.
//close the server and bail
svr.Active := False;
Exit;
except
//whoops, port's in use (or some related failure)
Result := False;
end;
finally
svr.Free;
end;
end;
//search a range of ports for the first available
function FindAvailablePort(First, Count: Word): Word;
var
svr: TPortChk;
bnd: TIdSocketHandle;
begin
//assume our initial port is available
Result := First;
//create our checking object
svr := TPortChk.Create;
try
//set the execute event
svr.OnExecute := svr.OnExec;
//loop looking for an available port
while (Result - First) < Count do begin
try
//set up the binding for our local system and the
//port in question
bnd := svr.Bindings.Add;
bnd.IP := '127.0.0.1';
bnd.Port := Result;
//try to bind. This will throw an EIdCouldNotBindSocket
//exception if the port is already in use.
svr.Active := True;
//if we get here, we found our available port, so kill the
//server and bail
svr.Active := False;
Exit;
except
Inc(Result);
svr.Bindings.Clear;
end;
end;
//if we get here, all of our possible ports are in use,
//so return $FFFF to indicate that no port is available
Result := $FFFF;
finally
svr.Free;
end;
end;

Resources