Delphi Indy https request with custom DNS resolving - delphi

example url:
https://api.binance.com/api/v1/time
Using TIdDNSResolver and cloudflare dns I can get the host IP.
direct request in the form of
https://205.251.219.20/api/v1/time
doesn't work cause as I understand the server expects to see "api.binance.com" in the url. (it doesnt work even in browser)
Using synapce and the following patch to replace request's host with resolved IP I make it working
function THTTPSend.InternalDoConnect(needssl: Boolean): Boolean;
begin
Result := False;
FSock.CloseSocket;
FSock.Bind(FIPInterface, cAnyPort);
if FSock.LastError <> 0 then
Exit;
If ResolvedIP.IsEmpty
then FSock.Connect(FTargetHost, FTargetPort)
else FSock.Connect(ResolvedIP, FTargetPort); // !!
Is there any way to do the same using Indy?

By default, TIdHTTP uses the Host:Port that is specified/inferred by the URL that is being requested. To change that behavior, you would have to alter TIdHTTP's source code, such as in the TIdCustomHTTP.SetHostAndPort() or TIdCustomHTTP.CheckAndConnect() method (which are both not virtual), and then recompile Indy.
Alternatively, you could use TIdHTTP's OnSocketAllocated or On(Before|After)Bind event (which are not promoted to published in TIdHTTP, so you would have to access them manually at runtime) to change the TIdHTTP.IOHandler.Host property to whatever IP address you want to connect to. This will not have any affect on the HTTP headers that TIdHTTP sends, such as Host, which will still be taken from the URL.
Alternatively, if you want all of Indy's Host-to-IP DNS queries to go through Cloudflare, you could derive a custom class from TIdStack (or whatever platform-specific descendant you are interested in, such as TIdStackWindows), override its virtual HostByName() method to perform whatever DNS query you want, and then pass your class type to the SetStackClass() function in the IdStack unit at program startup before any Indy classes are instantiated.

Related

How to use TIdWhois with SOCKS proxy

I'm getting whois information using this code:
IdWhois1.Host := 'whois.nic.tld';
ServerResultStr := IdWhois1.WhoIs('google.tld');
But I need to query the whois server (port 43) using a SOCKS proxy server.
I'm using Delphi 10.3.3. Is there any way to achieve this by using TIdWhois?
SOCKS is handled in Indy using the TIdSocksInfo component. You can configure it with the proxy details as needed (Host, Port, Version, Authentication, etc).
To make TIdWhois (or any TCP client) connect to its target server through TIdSocksInfo, you need to do the following:
assign a TIdIOHandlerSocket-derived component (TIdIOHandlerStack, or any TIdSSLIOHandlerSocketBase-derived component, like TIdSSLIOHandlerSocketOpenSSL) to the TIdWhois.IOHandler property.
assign the TIdSocksInfo to the IOHandler's TransparentProxy property.

Delphi Web.HTTPApp.TWebRequest RemoteIP VS RemoteAddr

TWebRequest has two metnod for retrive the client IP Address:
RemoteIP
RemoteAddr
From docs of RemoteIP:
Specifies the IP of the remote target machine associated with the HTTP
request message. Read the RemoteIP property to obtain the IP address
of the remote target machine associated with the HTTP request message.
From docs of RemoteAddr:
Indicates the remote IP address of the client associated with the HTTP
request message. Read RemoteAddr to obtain the IP address of the
source of the Web client request.
They seem very close but RemoteIP return an empty string.
I want retrieve the client ip address, what is the right method?
If we look into source code we will find the following in the declaration of TWebRequest
property RemoteIP: string read GetRemoteIP;
property RemoteAddr: string index 21 read GetStringVariable;
Implementation:
function TWebRequest.GetRemoteIP: string;
begin
Result := EmptyStr;
end;
and GetStringVariable is a virtual method.
Let's look into one of child classes - TISAPIRequest, we will find the following:
LResult := GetFieldByNameA(ServerVariables[Index]);
ServerVariables[21] is 'REMOTE_ADDR' header, which shows IP of client or proxy server.
But there is no implementation for GetRemoteIP.
It looks like RemoteIP isn't used in requests, because TCGIRequest, TApacheRequest and TWinCGIRequest also don't implement GetRemoteIP.

How to filter requests on Delphi datasnap rest server?

I'm new to this delphi datasnap stuff and there are barely any useful documentations about it.
I've setup a simple rest server by following this guide. Now I want to define my own authentication protocol.
As far as I know. All requests came from remote clients will be processed through WebModule methods before invoking server class methods. So far, I was able to capture the client IP adress and the requesting URL path like this:
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
ShowMessage(request.RemoteAddr); // adress where request came from
ShowMessage(request.PathInfo); // url path
Response.SetCustomHeader('access-control-allow-origin','*');
if FServerFunctionInvokerAction <> nil then
FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;
How am I able to:
Reject requests depend on specific IP addresses and return 401 status code to clients before they reach the class methods?
Redirect client to another methods? For example: Redirect this resquest:
request = new XMLHttpRequest();
request.open('GET', 'http://localhost:8080/datasnap/rest/TServerMethods1/ReverseString/blah', true);
To another method instead of ReverseString.
Complex redirection scenarios can be handled with an Apache HTTP server as reverse proxy with the mod_rewrite module.
For example, this answer shows that client IP addresses can be used to trigger a redirection.
https://stackoverflow.com/a/1073877/80901
RewriteCond %{REMOTE_ADDR} !^10\.0\.1\.1$
RewriteRule ^ /maintenance.html
this would send all requests with some exception to a maintenance page.
Using Apache (or a different HTTP proxy server) also allows to keep the DataSnap server running continuously, while Apache configuration changes are applied. This will remove your server down time virtually to zero.

Delphi SOAP server couldn’t handle SOAPAttachemnt after a non-exist method is invoke

I found this problem while I solving another SOAP problem and just post here to see if anyone experienced it or have any recommendation.
The problem:
SOAP server will not process method that receive TSOAPAttachment object after a non-exist method is invoked.
The TSOAPAttachment.SourceStream become inaccessible and TSOAPAttachment.CachFile is empty after the non-exist method is invoked
BorlandSoapAttachment(n) temp file is locked and piling up when the problem happen.
Back to normal after restarting IIS or recycling the App Pool
The steps of create this problem:
Create a simple Soap client/server application with 3 methods initially, one of them should receive and return a TSOAPAttachment, the other 2 can be anything, int or string.
I actually followed this article http://blogs.embarcadero.com/pawelglowacki/2008/12/18/38624 to create the simple soap application, but add a method to receive and return TSOAPAttachment. So my Interface looks like this:
code
TSimpleCalculator = class(TInvokableClass, ISoapCalculator)
private
public
function add(a, b: Integer) : Integer; stdcall;
function subtract(a, b: Integer): Integer; stdcall;
function TestRequest(const Request: TSOAPAttachment): TSOAPAttachment; stdcall;
end;
Create a client of this Soap server to invoke all three methods.
Everything should work happily at this point, ie. the client can invoke all 3 methods and the server could response to all these successfully.
Now, I remove the Subtract function in the server, but still using the original client.
Invoking both Add and TestRequest still complete successfully.
However, after I invoke the non-exist Subtract function, TestRequest will not work anymore but the Add function still work.
The TestRequest will have all the problem I describe above, ie. the server still response to it, but it cant get access to the TSOAPAttachment object.
Any advise is appreciated.
I used soap before, but from Java, not Delphi. But because SOAP is ubiquotus, it does not matter, it has to follow the SOAP specification. The SOAP server should return SOAP error as reply from a SOAP request that does not confront to the published methods. Your Delphi SOAP server should generate SOAP error and your Delphi SOAP client should handle the error properly. You can use a TCP traffic monitor and assign a filter so it only shows a SOAP traffic or use a SOAP traffic monitor. With a monitor you can see the request send to the server (including the message) and the reply as well. You can see the W3C online documentation about the SOAP specification. After all, your SOAP application must follow the spec.

Delphi XE2: How to define custom DataSnap REST URI?

I am using Delphi XE2 to write DataSnap REST service. I notice that the REST URI in DataSnap must strictly follow this format (refer here):
http://my.site.com/datasnap/rest/URIClassName/URIMethodName[/inputParameter]*
A famous example is sample method create by DataSnap server wizard:
http://my.site.com/datasnap/rest/TServerMethods1/ReverseString/ABC
There are 2 common ways to supply parameters in URI:
Path Segment parameter: /TServerMethods1/ReverseString/ABC
Query String parameter: /TServerMethods1/customers?name=bill
The Path Segment parameter URI is definitely supported by DataSnap REST. Is Query string parameters URI support in DataSnap REST too?
I have the following REST URI example and found it seems impossible to make it work with current DataSnap REST library:
/customers/A1234
return customer object of ID A1234
/customers/A1234.xml
return customer object of ID A1234 in XML format
/customers/A1234.json
return customer object of ID A1234 in json format
/customers/A1234.html
return customer object of ID A1234 in html format
/customers?name=Bill
return a list of customer whose name contain Bill
I don't know how to do it using DataSnap, but there are ways around it. You can put something called URLRewrite to good use for this as both your friendly URI's and the ones required by DataSnap are easily mappable.
For IIS you can use (enable) the URLRewrite module which is standard in IIS 7. More information can be found on the official site: http://www.iis.net/download/urlrewrite.
Be sure to create rules for inbound and outbound URI's so that the "internal" (Datasnap) URI's do not get out into the wild.
If you are running the site on Apache, similar functionality is available, and I thin you need to amend the .htaccess file, but I have no experience with Apache so I could be wrong.
A bit late to the party, but yes you can use query parameters.
You have to use GetInvocationMetadata.QueryParams
see the example below.
uses DBXPlatform;
function TServerMethods1.EchoString(Value: string): string;
var
metaData: TDSInvocationMetadata;
i: integer;
begin
metaData := GetInvocationMetadata;
for i := 0 to Pred(metaData.QueryParams.Count) do
begin
Result := Result + '<param>' + metaData.QueryParams[i] + '</param>';
end;
metaData.ResponseContent := '<xml>' + Result + '</xml>';
end;

Resources