TIdHTTP indy component and IPVersion property (firemonkey) - firemonkey

i am using the TIdHTTP component on a multi-device app (building in Rad Studio Tokyo 10.2.3). All i'm doing is downloading a file to my local app folder (iOS). I want to make sure it works with IPv6 but i don't see the "IPVersion" property for TIdHTTP. I see it on other indy components in rad studio (e.g. IdFTP).
Is there a way to set IPVersion in code for the TIdHTTP component? Below is a snip of the code i'm using to download the file. If it fails on IPv4 it is supposed to try IPv6 next:
UnicodeString LFileName = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(), "myfile.txt");
TFileStream* fs = new TFileStream(LFileName, fmCreate);
Form1->TIdHTTP->ConnectTimeout = 8000; // give it 8 seconds
Form1->TIdHTTP->ReadTimeout = 8000;
try
{
UnicodeString URL = "http://myservername.com/myfile.txt";
Form1->TIdHTTP->Get(URL, fs);
Form1->TIdHTTP->Disconnect();
// ShowMessage("Good download via IPv4");
}
catch(const System::Sysutils::Exception &)
{
try
{
Form1->TIdHTTP->Disconnect(); // clean up from IPv4 try
UnicodeString URL = "http://[myservername.com]/myfile.txt";
Form1->TIdHTTP->Get(URL, fs);
Form1->TIdHTTP->Disconnect();
// ShowMessage("Good download via IPv6");
Righ now i just put brackets around the domain name in hopes that this would work for IPv6...i won't know for sure until i can get a truly IPv6 only network setup (working on it).
UPDATE: I just had an app accepted to Apple Store that uses this approach so obviously it passed IPv6 testing. FYI

I found this discussion in which Remy Lebeau states:
TIdHTTP parses IP version info only from the URL itself, so if you need to send a request to an IPv6 target (IP address or hostname), you have to tell TIdHTTP to use IPv6 by wrapping the hostname portion of the URL in square brackets, eg:
s := idHTTP.Post('http://[IPv6 address or hostname]/resource', ...);
Otherwise TIdHTTP uses IPv4 instead.
This bracketed syntax is a requirement when using IPv6 address literals in
URLs, per RFCs 2732 and 3986. But when using a hostname instead, there is
no such syntax requirement. Connecting to a hostname requires a DNS lookup
to discover the host's available IP addresses and their respective IP versions, and then connect to those addresses as needed until one succeeds.
Indy does not currently implement that kind of connection model. It first
allocates a socket based on the client's IPVersion property, THEN performs
a DNS request suited for that IPVersion, and then finally connects to the
first IP address reported by DNS. This is a known limitation of Indy, which
would require a redesign of Indy's internal logic to fix. However, it can
be worked around manually in most Indy client components by performing
your own DNS lookup ahead of time and then looping through the results, setting Indy's Host and IPVersion properties and calling Connect() on each loop iteration.
However, TIdHTTP is a special case, because it derives the Host and IPVersion values from the requested URL, overriding anything the calling code might set manually. However, it is also because of this model that would potentially allow TIdHTTP to be updated to perform its own DNS loop internally without having to rewrite all of Indy's client components to match. Except that Indy does not currently implement a function that returns all of the IP addresses of a hostname, only the first IP address of a particular version. Until such a function is added to Indy in a cross-platform manner, TIdHTTP cannot be updated to auto-detect IPv4/IPv6 hosts.
As such, this confirms my approach should work (trying IPv4 first and then IPv6 if that fails) using the brackets.
Also, I edited my code to show a "Disconnect" prior to trying the IPv6 in case the IPv4 left a mess.

Related

Connecting to MySQL (on Google Cloud SQL) via JDBC and IPv6?

I would like to connect to Google Cloud SQL from an external application using JDBC and the instance's IPv6 address as shown on my Google Developers Console (here obfuscated):
String url = "jdbc:mysql://abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd";
String user = "root";
String password = "<also_obfuscated>";
connection = DriverManager.getConnection(url, user, password);
This leads to the following exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
Cannot load connection class because of underlying exception:
'java.lang.NumberFormatException:
For input string: "abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd"'.
I am using the latest JDBC driver for MySQL. Connection via JDBC and IPv4 works but requires an extra configuration step and incurs (small) extra cost.
So is it even possible to connect to MySQL via JDBC and IPv6 and if so how?
UPDATE According to the documentation, this URL should work for IPv6:
jdbc:mysql://address=(protocol=tcp)(host=abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd)(port=3306)
However, now I'm getting the following exception:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago.
The driver has not received any packets from the server.
Besides the JDBC driver supporting IPv6 (which it does, according to the documentation), and the client OS supporting IPv6 (which mine should as it is OS X Yosemite), and the server OS supporting IPv6 (which the Google Cloud does because it reports an IPv6 server address) what other pieces need to be in place in order for IPv6 client-server connections to work?
E.g. does my ISP have to provide any particular support?
You need to register the IPV6 address from which you'll be coming into Google Cloud SQL, among the authorized addresses on the Cloud SQL console.
You can check that IPv6 address e.g by visiting sites such as whatismyv6.com .
Then, all your ISP has to do is to provide a stable IPV6 address (alas, even to these days, not all do -- alas, AT&T Uverse, my ISP at home, does not, for example).
Even from locations where I could reliably get a stable IPv6 address, I had exactly the same problem, originally -- until it dawned on me that, if I'm coming in with an IPv6 address and what I've authorized is an IPv4 one, Google Cloud SQL cannot "translate" one into the other to find out I'm in fact authorized!-)
Note that if you don't have and can't get an IPv6 address from which to connect (e.g. connecting from a home machine through an ISP that does not yet support IPv6) then you can hit the "Request IPv4 address" button under Google Developers Console / Storage / Cloud SQL / [Your Intance] / Access Control / IP Address and you will get one assigned (within a few seconds) which will cost $0.01 per hour, paid from your free $300 of credit if you still have that credit available. Once you move your app to, for example, one of Google's app servers, you will no doubt be able to get an IPv6 address. Release the IPv4 address when unused to save credit.
Try
String url = "jdbc:mysql://[abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd]";
In URLs literal IPv6 addresses have to be surrounded by [] so that the parser can see the difference between parts of the address and the optional port number which is also separated by a :.
It is often easier to use hostnames instead of literal IP addresses. It stays independent of the used IP protocol and changing addresses is easier as well.

Ruby: force dns resolution through custom dns server

I want to resolve the DNS requests issued from within a Ruby script through a DNS server, different from the ones in resolv.conf. While I could do that manualy by using Resolv::DNS or something like that, I'd like to do that for all the requests (like the ones issued by RestClient, for example). Any ideas?
RestClient uses net/http and uses the host name part of the provided URL to open a TCP socket:
https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb?source=cc#L879
The simplest way to change which host is accessed is to manually change the URL to use an IP address by performing the lookup yourself.
Alternatively, you can replace the resolver of the various *Socket classes, and there is actually an example of how to do this here: https://github.com/ruby/ruby/blob/4c2304f0004e9f1784540f3d36976aad9eab1f68/lib/resolv-replace.rb

Clear WinInet DNS cache programmatically

WinINet library caches IP address for any URL accessed over it. Because of this, when IP address for that URL gets changed then also WinInet library's HttpSendRequest goes to older IP address.
And, if older IP is responding, then WinINet will send all http request to older IP only.
Is there any way to force clean DNS cache of WinInet?
Or
Is there any way to force WinINet to send HTTP request to specified IP address (as we are able to get newer IP using gethostbyname())?
Note :
gethostbyname is giving me newer IP address, So this behaviour is happening of WinINet's caching.
I have tried "method 2" suggested in this MS article, but it didn't help
Sample code
You can try to use the flag when connecting:
INTERNET_FLAG_DONT_CACHE = 0x04000000 Does not add the returned entity
to the cache. This is identical to the preferred value,
INTERNET_FLAG_NO_CACHE_WRITE.
Or you can take a look at the DeleteUrlCacheEntry from the WinInet documentation here
I beleave this should do the trick.
--UPDATE
From this doc I've seen that there is an better flag to use it, look at the:
INTERNET_FLAG_PRAGMA_NOCACHE
Forces the request to be resolved by the origin server, even if a cached copy exists on the proxy.
--UPDATE
As tested by #Pradeep you can change this registry keys to work it:
HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\DnsCacheEnabled
ServerInfoTimeOut and DnsCacheTimeout to 0.

Setting up a server

One of my real weak points in programming is networking, so I admit that I may be a little over my head with this project. Please feel free to tell me if what I'm trying to do doesn't make any sense
What I am trying to do, basically, is run a program on my laptop (Node.JS, probably) that handles requests from a website, does some functions, and serves data back to a client running on the website. (Research tells me this is called an RPC server)
When you listen for requests in Node.JS, you specify a port and optionally an IP Address- localhost, 127.0.0.1, is what all the tutorials I've read have used, but that's not sufficient for what I'm trying to do
I've read that I'll need to set up a static IP Address? But I think those are relative to my LAN, so they'll be like 192.168.0.X. So then what would I specify for the IP for the server and the client? (I don't think the port particularly matters). Do I need a DNS?
I hope this makes sense, sorry for so many questions, thank you for your help
You can run a server on your local machine, and you will specify your local IP address for the script, like 192.168.0.x. But for this server to ever receive a connection, your client must connect to your external IP address. It is the IP address that you get from your Internet provider when you connect to Internet. If your external IP is static, i.e. it does not change, then you can use it in your client script. If the external IP changes, you must setup a DNS record that would resolve the name of your computer. DynDNS can be used for that purpose.
If you have a router, it must be setup so that it forwards connections to your laptop where the server runs. And your firewall must be configured to allow connections.

Socket.Bind and IP source routing, with multiple local network interfaces

I wrote a tool running on a system (Win7) with two network interfaces, each linked to a different subnet, each with its own gateway which is then linked to two separate distant networks (there are outgoing firewalls after each gateway). I’m initiating outgoing TCP connections via both NICs by using Socket.Bind (before doing Connect) to each relevant NIC’s IP address. First NIC is working fine, but for the second NIC, I’m getting SocketException: “A socket operation was attempted to an unreachable network”.
My original understanding was that since sockets are bound to concrete NIC’s local endpoint, which has its gateway defined, the connection should be routed to this gateway and therefore should work. However, it seems that source IP address is ignored and the routing is working according to local routing table (i.e. second NIC’s connect request goes to first, default, network and being rejected because it has wrong subnet).
Adjusting local routing tables helps, but it makes me wonder about the whole reasoning behind ability of the socket to bind to specific local IP.
Doing some extra reading, I found out that, indeed, there’s such thing as “source IP routing”, but it is disabled in Windows by default (via DisableIPSourceRouting registry setting), due to security reasons, as described, e.g. here:
http://msdn.microsoft.com/en-us/library/ff648853.aspx
http://www.bloggersbase.com/disableipsourcerouting/
Questions:
If my original understanding was correct (i.e. Socket.Bind should be enough) – why it is not working without modifying routing tables?
If my understand was NOT correct (i.e. Socket.Bind is ignored and routing is used) – what’s the point of having Socket.Bind? Why doing it at all?
Also, I’d like to understand better, what is the actual risk of having source IP routing enabled (preferably with example of a possible exploit)?
Any ideas of solving the requirement without manually modifying local routing table will be greatly appreciated.
Many thanks.
OK, after some reading, here are some high-level explanations on what's happening. I still need to verify the below conclusions in my system. Apparently, local binding is typically ignored when selecting network interface. Instead, routing table is used for this. However, in Strong Host Model (default for Vista and newer, non-existant in XP), source IP is used as a 'constraint' in the routing table lookup.
Brief explanation about strong host model vs. weak host model:
http://technet.microsoft.com/en-us/magazine/2007.09.cableguy.aspx
Explanation on what's different in XP vs newer Windows versions in respect to the above:
http://blogs.technet.com/b/networking/archive/2009/04/24/source-ip-address-selection-on-a-multi-homed-windows-computer.aspx

Resources