Access localhost via internet - delphi-7

I've created a HTTP server via the IdHTTP-component and now I want to access it via the internet. I've posted a string from a text file.
I can accesss it via:
http://localhost
But how do I acces it via the internet? I've tried http://[myexternalIPaddress]:80 but I do not get a reply.
This is my code:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
myFile : TextFile;
text: String;
begin
AssignFile(myFile, 'C:\Users\xxx\Desktop\test.txt');
Reset(myFile);
ReadLn(myFile, text);
AResponseInfo.ContentText := text;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdHTTPServer1.Active := True;
end;

You need to configure your Windows Firewall (if enabled) to allow inbound connections to port 80, and you also need to configure your network router (if you have one) to forward inbound connections to port 80 on your router's public IP to port 80 on your server machine.

Related

How to connect to a IPv6 link local address discovered using IPv4 and set the correct network interface?

Our Delphi/Indy application discovers devices using mDNS on IPv4 but some of the addresses found are link-local IPv6 addresses (specified in AAAA records along with other A records). We want to provide the option of using either IPv4 or IPv6 addresses to connect to the devices because some people insist on setting static IPv4 addresses which due to a change in location or configuration become incompatible with the subnet where they are located.
But when the application attempts to establish a TCP connection to one of these link-local IPv6 addresses, the first attempt usually fails because Windows doesn't know which interface to use. (This same behaviour can be observed when just ping-ing the address from the command line. The problem is solved by writing % followed by the appropriate zone ID at the end of the link-local address.)
The TIdTCPClient object being used to connect to the device therefore needs to be associated with the correct network interface. I understand that this can be done by setting the BoundIP property but I do not have the IPv6 address of the interface where the device was discovered because this was done using IPv4. Is there a way of using the TIdSocketHandle object that was provided in the OnUDPRead event when the device was discovered and using that to set the appropriate thing in the TIdTCPClient object?
But when the application attempts to establish a TCP connection to one of these link-local IPv6 addresses, the first attempt usually fails because Windows doesn't know which interface to use.
Are you setting the TIdTCPClient.IPVersion property to Id_IPv6? Indy does not currently determine the IP version to connect with based on the IP address specified (ticket), you have to explicitly specify the IP version up front.
Most of the time, it should be good enough to let Windows decide which interface to use when connecting to an IP address. That is what its routing tables are meant for.
This same behaviour can be observed when just ping-ing the address from the command line. The problem is solved by writing % followed by the appropriate zone ID at the end of the link-local address.
Indy does not currently support zone/scope IDs for IPv6 addresses (ticket).
Is there a way of using the TIdSocketHandle object that was provided in the OnUDPRead event when the device was discovered
Not likely, because you said the discovery was made over IPv4, which means the TIdSocketHandle's UDP socket was bound to an IPv4 interface, not an IPv6 interface. The only possibility would be if the interface in question has both IPv4 and IPv6 addresses assigned to it, in which case you could use Win32 APIs to match up the TIdSocketHandle.IP to a specific interface, and then enumerate the IPv6 addresses on that same interface.
Otherwise, you can use Indy's TIdStack.GetLocalAddressList() method to get all of the local IPv6 interface IPs, and then bind the TIdTCPClient to each one in a loop until a connection to the target device is successful.
Taking Remy's advice, I implemented the following:
uses Winapi.IpTypes, IdGlobal, IdWinsock2, IdStackBSDBase;
function GetAdaptersAddresses(Family: ULONG; Flags: DWORD; Reserved: PVOID; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; var OutBufLen: ULONG): DWORD; stdcall; external 'iphlpapi.dll';
// Given an IPv4 address of a network interface, this returns one of the IPv6 addresses of the same interface.
function GetAdapterIPv6Address (IPv4Address : String) : String;
const
flags = GAA_FLAG_SKIP_ANYCAST or GAA_FLAG_SKIP_MULTICAST or GAA_FLAG_SKIP_DNS_SERVER or GAA_FLAG_SKIP_FRIENDLY_NAME;
var
BufLen : ULONG;
Ret : DWORD;
Adapter, Adapters : PIP_ADAPTER_ADDRESSES;
UnicastAddr : PIP_ADAPTER_UNICAST_ADDRESS;
UnicastAddr6 : PIP_ADAPTER_UNICAST_ADDRESS;
Found : Boolean;
i : Integer;
a : UInt32;
begin
result := '';
a := IPv4ToUInt32(IPv4Address);
BufLen := 0;
Adapters := nil;
Ret := GetAdaptersAddresses(PF_UNSPEC, flags, nil, nil, BufLen);
if (Ret = ERROR_INSUFFICIENT_BUFFER) or (Ret = ERROR_BUFFER_OVERFLOW) then begin
Adapters := AllocMem(BufLen);
try
Ret := GetAdaptersAddresses(PF_UNSPEC, flags, nil, Adapters, BufLen);
if Ret = ERROR_SUCCESS then begin
Adapter := Adapters;
repeat
UnicastAddr6 := nil;
Found := false;
if (Adapter.IfType <> 24 {IF_TYPE_SOFTWARE_LOOPBACK}) and ((Adapter.Flags and IP_ADAPTER_RECEIVE_ONLY) = 0) then begin
UnicastAddr := Adapter^.FirstUnicastAddress;
while UnicastAddr <> nil do begin
if UnicastAddr^.DadState = IpDadStatePreferred then begin
case UnicastAddr^.Address.lpSockaddr.sin_family of
AF_INET : begin
with TIdIn4Addr(PSockAddrIn(UnicastAddr^.Address.lpSockaddr)^.sin_addr) do
if a = ((S_un_b.s_b1 shl 24) or (S_un_b.s_b2 shl 16) or (S_un_b.s_b3 shl 8) or S_un_b.s_b4) then
Found := true;
end;
AF_INET6 :
if UnicastAddr6 = nil then
UnicastAddr6 := UnicastAddr;
end;
end;
UnicastAddr := UnicastAddr^.Next;
end;
end;
Adapter := Adapter.Next;
until (Adapter = nil) or found;
if found and assigned(UnicastAddr6) then begin
for i := 0 to 7 do begin
if i > 0 then
result := result + ':';
with TIdIn6Addr(PSockAddrIn6(UnicastAddr6^.Address.lpSockaddr)^.sin6_addr) do
result := result + IntToHex(ntohs(s6_addr16[i]), 1);
end;
end;
end;
finally
FreeMem(Adapters);
end;
end;
end;
Due to the device discovery being done via IPv4, the IPv4 address of the network interface is known (IP property of TIdSocketHandle) so by using the above function, TIdTCPClient.BoundIP can be set so that the network interface is unambiguous for any link-local IPv6 address found in the discovery process.

Indy 10 TIdUDPclient - detect wrong/no Answer

Using delphi 10.3 and JEDI VCL.
I have a communication with a device, which responds to UDP data.
Now I want to be able to check if I got an answer from the right device, or if if I even got any answer.
Currently I am using the following:
function TDIB.ReadData(ACommandCode: WORD; ASendLength : Cardinal; AReceiveLength : Cardinal; AAddress : Cardinal) : Integer;
var
cmdHeader : PDIBCommandHeader;
UDPSend, UDPRecv : TIdBytes;
client : TIdUDPClient;
begin
gRequestPending := TRUE;
// Reserviere Speicher
SetLength(UDPSend, SIzeOF(TDIBCommandHeader) + Cardinal(ASendLength));
SetLength(UDPRecv, SIzeOF(TDIBCommandHeader) + Cardinal(AReceiveLength));
cmdHeader := #UDPSend[0];
cmdHeader.Init(WORD(ACommandCode), AAddress, MAX(ASendLength, AReceiveLength));
client := TIdUDPClient.Create();
try
client.Host := ValToIPv4(gDIBAddress);
client.Port := TDIBPorts.mainPort;
client.Active := TRUE;
client.sendBuffer (UDPSend);
client.ReceiveBuffer(UDPRecv,TDIB.C_CMDTimeout);
except
on E: Exception do
begin
ShowMessage('Exception');
client.Free;
end;
end;
SetLength(lastUDPData, Length(UDPRecv));
move (UDPRecv[0],lastUDPData[0],Length(UDPRecv));
client.Free;
gRequestPending := FALSE;
end;
Which is fine when the client is responding, but I am not catching any misbehaviour, like when the host machine tries to reach the client and the client is not responding.
From the documentation of Indy10 I am missing something like TIdUDPClient.TimedOut or like that.
I want to be able to tell, if the client is not responding after Xms after I sent the UDP packet and I want to be able to check, if the sender address is the wanted client IP.
I want to be able to tell, if the client is not responding after Xms after I sent the UDP packet
ReceiveBuffer() returns the number of bytes actually received. If no packet is received within the specified timeout, ReceiveBuffer() will return 0.
I want to be able to check, if the sender address is the wanted client IP.
Use one of the ReceiveBuffer() overloads that has a VPeerIP output parameter. That will give you the sender IP if a packet is received, or it will give you an empty string if no packet is received.
Do be aware that UDP has a concept of a 0-byte datagram. ReceiveBuffer() will return 0 for that as well. In the case that 0 is returned, you can use this output string to differentiate between no packet received (VPeerIP = '') vs a 0-byte packet received (VPeerIP <> ''), if needed.

How can create https server using synapse in lazarus

I am trying to create https server in lazarus using synapse but I am failing. I want to mys server receive data from other https clients.
I am sending requests with my browser using https://localhost:1500 and mys server is receiving signals. But when I try to read sent data I receive nothing. When I tested simple http server all worked fine. But now in case of https it is not working. I am using ubuntu 15.04 as my OS
s := ASocket.RecvString(timeout); //returns noething
My sample code:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
uses
blcksock, sockets, Synautil, ssl_openssl, ssl_openssl_lib;
procedure AttendConnection(ASocket: TTCPBlockSocket);
var
timeout: integer;
s: string;
method, uri, protocol: string;
OutputDataString: string;
ResultCode: integer;
begin
timeout := 1000;
WriteLn('Received headers+document from browser:');
//read request line
s := ASocket.RecvString(timeout);
WriteLn(s);
method := fetch(s, ' ');
uri := fetch(s, ' ');
protocol := fetch(s, ' ');
//read request headers
repeat
s := ASocket.RecvString(Timeout);
WriteLn(s);
until s = '';
// Now write the document to the output stream
if uri = '/' then
begin
// Write the output document to the stream
OutputDataString :=
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + CRLF
+ '<html><h1>Teste</h1></html>' + CRLF;
// Write the headers back to the client
ASocket.SendString('HTTP/1.0 200' + CRLF);
ASocket.SendString('Content-type: Text/Html' + CRLF);
ASocket.SendString('Content-length: ' + IntTostr(Length(OutputDataString)) + CRLF);
ASocket.SendString('Connection: close' + CRLF);
ASocket.SendString('Date: ' + Rfc822DateTime(now) + CRLF);
ASocket.SendString('Server: Servidor do Felipe usando Synapse' + CRLF);
ASocket.SendString('' + CRLF);
// if ASocket.lasterror <> 0 then HandleError;
// Write the document back to the browser
ASocket.SendString(OutputDataString);
end
else
ASocket.SendString('HTTP/1.0 404' + CRLF);
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
ListenerSocket, ConnectionSocket: TTCPBlockSocket;
begin
ListenerSocket := TTCPBlockSocket.Create;
ConnectionSocket := TTCPBlockSocket.Create;
ListenerSocket.CreateSocket;
ListenerSocket.SSL.CertificateFile := '/home/imants/projects/apps/medieval/bin/40669199_localhost_8080.cert';
ListenerSocket.SSL.PrivateKeyFile := '/home/imants/projects/apps/medieval/bin/40669199_localhost_8080.key';
ListenerSocket.SSLDoConnect;
ListenerSocket.setLinger(true,10);
ListenerSocket.bind('localhost','1500');
ListenerSocket.listen;
repeat
if ListenerSocket.canread(1000) then
begin
ConnectionSocket.Socket := ListenerSocket.accept;
WriteLn('Attending Connection. Error code (0=Success): ', ConnectionSocket.lasterror);
AttendConnection(ConnectionSocket);
ConnectionSocket.CloseSocket;
end;
until false;
ListenerSocket.Free;
ConnectionSocket.Free;
end;
end.
There are two sources, that I know of, with an example for a HTTP(s) server in Synapse.
The first example is in the Synapse stable package (release 40). Although I would recommend you use the SVN version (you can use the Download Snapshot button on that page) you can still use the examples in the "release 40 package".
The example in synapse40\source\demo\httpsserv should be usable as HTTPS-server. If it's not you could take the httpserv (HTTP) example and change it as shown here. (But I think the httpsserv is just the same with those modifications)
If you're on Linux (Lazarus) you'll need to change every occurrence of winsock to synsock and remove any windows-clause.
Another example can be found here. (Direct download of SynHttp.zip) As far as I could see it also has HTTPS-server functionality.
I know that you want to use Synapse, but you may want to take a look at Indy. I have been developing server/client apps with indy for some years and I like it. It's working fine with windows and linux (32bit, 64bit, arm...) and has some nice features. You could use the TIdHTTPServer component. Moreover get an IOHandlerSSLOpenSSL. The event OnCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo) is the most important one. It allows access to the Request (like A ARequestInfo.Document) and to your HTTP response (AResponseInfo).

Inno Setup detect Windows Firewall state

I need to detect the Windows Firewall state (i.e. whether it is enabled or not) in order to display a message warning that a Firewall rule may need to be configured to allow inbound connections on specific ports when the Firewall is enabled, but not when it isn't. See below code example:
[Code]
//Check if Windows Firewall is enabled
function IsWindowsFirewallEnabled(): Boolean;
begin
//Method required here
Result := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
//Display a warning message on a Server install if Windows Firewall is enabled
if CurPageID = wpSelectComponents and IsComponentSelected('Server') and IsWindowsFirewallEnabled then
begin
MsgBox('Windows Firewall is currently enabled.' + #13#10 + #13#10 +
'You may need to enable inbound connections on ports 2638, 445 and 7.'
mbInformation, MB_OK);
Result := True;
end;
end;
What I need is a method for the IsWindowsFirewallEnabled function. One way I have read about, and ironically has now more or less been suggested below whilst I was in the middle of updating the question with this information anyway, would appear to be reading the EnableFirewall value from the Registry:
//Check if Windows Firewall is enabled
function IsWindowsFirewallEnabled(): Boolean;
var
crdFirewallState: Cardinal;
begin
RegQueryDwordValue(HKLM, 'SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\StandardProfile',
'EnableFirewall', crdFirewallState);
if crdFirewallState = 1 then
Result := True;
end;
However, I am not convinced by this method as the Registry values for all the profiles show enabled on my work PC, but looking in Control Panel the Domain profile shows disabled (I assume this is related to a Group Policy).
Note, that this needs to work for both Windows XP and Server 2003, and for Windows Vista and Server 2008 and above.
Therefore, what's the most reliable or recommended way to do this?
You would need to determine the registry entry and then query it in a manner similar to this using Innosetup's registry query ability.
var
Country: String;
begin
if RegQueryStringValue(HKEY_CURRENT_USER, 'Control Panel\International',
'sCountry', Country) then
begin
// Successfully read the value
MsgBox('Your country: ' + Country, mbInformation, MB_OK);
end;
end;
http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_regquerystringvalue
Allegedly this is the information for the registry key:
Path: HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile
Location: Local Machine
Value Name: EnableFirewall
Data Type: DWORD (DWORD Value)
Enabled Value: 0
Disabled Value: 1

How to get local IP4 address using Indy?

I'm using TIdStack.LocalAddress to get the local IP address. On an OS X system which has both IP6 and IP4 this returns the IP6 address, which isn't what I want.
What's the best way to find the local IP4 address? I could simply use the shortest entry in TIdStack.LocalAddresses, for example.
The TIdStack.LocalAddress property simply returns the first IP that is listed in the TIdStack.LocalAddresses property (note - these properties are deprecated because they are not thread-safe. You should use the TIdStack.AddLocalAddressesToList() method instead). A PC/device can have multiple local IPs, such as if it is connected to multiple networks, supports both IPv4 and IPv6, etc. The order of the IPs in the LocalAddresses list is determined by the OS, not by Indy. From the sounds of it, you will have to obtain the complete list and loop through it looking for the IPv4 address you are interested in.
TIdStackVCLPosix, which Indy uses for Mac OSX, is actually the only TIdStack implementation that currently supports reporting both IPv4 and IPv6 addresses in the LocalAddresses property (other TIdStack implementations only support IPv4 at this time). However, the list is a plain TStrings, it does not differentiate whether a given IP is IPv4 or IPv6 (it does not use the TStrings.Objects property to provide that info). If you need to differentiate, you will have to parse the IPs manually. In a future release, Indy will replace the TIdStack.LocalAddress(es) properties with a different implementation that natively provides the IP version info.
For example:
var
IPs: TStringList;
IP: String;
I: Integer;
Err: Boolean;
begin
IPs := TStringList.Create;
try
GStack.AddLocalAddressesToList(IPs);
for I := 0 to IPs.Count-1 do
begin
IP := IPs[I];
// TIdStack.IsIP() currently only supports IPv4, but
// it will be updated to support IPv6 in a future
// release...
//
// if GStack.IsIP(IP) then
// if GStack.IsIPv4(IP) then
IPv4ToDWord(IP, Err);
if not Err then
Break;
IP := '';
// alternatively:
{
IPAddr := TIdIPAddress.MakeAddressObject(IPs[I]);
IP := IPAddr.IPv4AsString; // returns blank if not IPv4
IPAddr.Free;
if IP <> '' then
Break;
}
end;
finally
IPs.Free;
end;
if IP <> '' then
begin
// use IP as needed...
end;
end;

Resources