I have been playing around with curl and connection-timeouts on windows.
To test the connect-timeout, i used a second machine with linux and dropped all packets for the ftp port using iptables.
When i try to connect from my Windows XP SP3 box using
"curl.exe --connect-timeout 5 ftp://linuxbox", curl will hang forever.
When i use the "--max-time 5" option instead, it aborts after 5 seconds.
I have traced the packets on the linux box using tcpdump and could see three SYN-packets. The second was three seconds after the first and the third was six seconds after the second.
No more than three packets arrive, no matter how long the timeout is set.
This seems to be, because the windows default for retransmissions is "3" (found here: http://support.microsoft.com/?scid=kb%3Ben-us%3B175523&x=6&y=7 Option TcpMaxConnectRetransmissions)
So even with "--max-time 50", only three SYN-Packets are sent by the Windows TCP/IP stack. Therefore every timeout greater 20 seconds is useless with windows.
I have tried this with the last-curl version, i found:
curl 7.19.3 (i686-pc-mingw32) libcurl/7.19.3 OpenSSL/0.9.8j zlib/1.2.3 libidn/1.11 libssh2/1.0
Protocols: tftp ftp telnet dict ldap http file https ftps scp sftp
Features: IDN Largefile NTLM SSL libz
To summarize everything:
1. The connect-timeout does not work at all.
2. The max-time option works, but setting it to higher than 20 seconds is useless, because only three SYN-Packets are sent anyway with the default windows setting.
Let me clarify, that i am only talking about the connection phase!
First, I know very little windows stuff so I've tried to get some help on this by forwarding this bug report to the libcurl mailing list: http://curl.haxx.se/mail/lib-2009-02/0225.html
I cannot understand how --max-time and --connect-timeout can differ for this case since they do the same things internally until the connect moment.
Regdarding the "TcpMaxConnectRetransmissions" that seems like a blatant windows stupidity that I'm not sure is our business to bother about. We use the TCP/IP stack as supposed and we do it the same for all platforms and if Windows decide to cripple their approach how are we going to deal with that?
A couple of questions...
You mention that you are using the last-curl version you've found. Where have you downloaded the binary from, please provide link? Or is it self-built?
Can you repeat the problem with a server publicly available so that others can repeat the problem that you're facing? Or is it enough to use a non-existing server to trigger the problem?
Does anything change if you use the IP address instead of the server name?
-=[Yang]=-
Thanks for your quick response!
I have investigated this a little further this morning by adding some debug infos and recompiling curl and libcurl and by using the "-v" commandline option.
The reason, why --max-time and --connect-timeout differ, is because curl obviously "thinks" it is connected so that the --connect-timeout is ignored. The --max-time simply aborts the connection because no data is transmitted.
I really agree with you, that this whole problem is just another "windows stupidity" !!
And i am, just like you, not sure, how to deal with it. Especially, since the "TcpMaxConnectRetransmissions" seems only to be changeable in the registry which would affect ALL connections (another "windows stupidity"!).
On the other hand is a not working "--connect-timeout" option not so nice too. So something should be done about this. If someone can confirm, what I have found out: what do you think about adding some hints to the documentation at least? I could provide you with that, if you wish.
Apart from that, I have at least found a workaround, which prevents curl from hanging forever.
It is in the Curl_socket_ready function in lib/select.c:
$ diff -p select.c.orig select.c
*** select.c.orig Mon May 26 17:16:34 2008
--- select.c Fri Feb 20 12:54:56 2009
*************** int Curl_socket_ready(curl_socket_t read
*** 285,290 ****
--- 285,297 ----
pending_tv.tv_usec = 0;
}
r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
+
+ #if defined(USE_WINSOCK)
+ if (WSAGetLastError() == WSAECONNRESET) {
+ r = -1;
+ }
+ #endif /* USE_WINSOCK */
+
if(r != -1)
break;
error = SOCKERRNO;
With this change, curl still thinks it is connected but aborts after the 20 seconds, when windows stops sending SYN-Packets.
I am not sure, if
"if (WSAGetLastError() == WSAECONNRESET) {"
can or should be
"if (WSAGetLastError() != ERROR_SUCCESS && WSAGetLastError() != WSAEWOULDBLOCK) {"
But maybe we can discuss this on the mailing list.
Dear Yang,
concerning your questions:
I used the precompiled version 7.19.2 from the Curl-Homepage and the source-code 7.19.3 which I have compiled myself using mingw.
I just tried it with a IP address that does not exist in my local network and got the same behaviour:
c:\programme\curl-7.19\bin\curl.exe -v --connect-timeout 5 ftp://10.42.254.169/
* About to connect() to 10.42.254.169 port 21 (#0)
* Trying 10.42.254.169... connected
* Connected to 10.42.254.169 (10.42.254.169) port 21 (#0)
Hanging forever ...
Using IP address or name makes no difference.
As you can see, curl thinks to be connected!
These are my findings so far...
All tests I mention here are done on a Windows 2000 SP4 box with _all_ WindowsUpdate patches applied.
Testing this binary http://curl.haxx.se/download/curl-7.19.3-win32-nossl.zip to a system which sends back a RST packet:
C:\test>curl.exe --version
curl 7.19.3 (i586-pc-mingw32msvc) libcurl/7.19.3 zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file
Features: Largefile libz
C:\test>curl.exe -v --trace-time --connect-timeout 8 ftp://192.168.1.100
04:46:15.390000 * About to connect() to 192.168.1.100 port 21 (#0)
04:46:15.390000 * Trying 192.168.1.100... Connection refused
04:46:16.331000 * couldn't connect to host
04:46:16.331000 * Closing connection #0
curl: (7) couldn't connect to host
C:\test>curl.exe -v --trace-time --max-time 77 ftp://192.168.1.100
05:04:19.619000 * About to connect() to 192.168.1.100 port 21 (#0)
05:04:19.629000 * Trying 192.168.1.100... Connection refused
05:04:20.590000 * couldn't connect to host
05:04:20.590000 * Closing connection #0
curl: (7) couldn't connect to host
Testing the same binary http://curl.haxx.se/download/curl-7.19.3-win32-nossl.zip to a non-existing system:
C:\test>curl.exe --version
curl 7.19.3 (i586-pc-mingw32msvc) libcurl/7.19.3 zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file
Features: Largefile libz
C:\test>curl.exe -v --trace-time --connect-timeout 8 ftp://192.168.1.101
04:49:17.952000 * About to connect() to 192.168.1.101 port 21 (#0)
04:49:17.952000 * Trying 192.168.1.101... Timeout
04:49:25.964000 * connect() timed out!
04:49:25.964000 * Closing connection #0
curl: (28) connect() timed out!
C:\test>curl.exe -v --trace-time --max-time 77 ftp://192.168.1.101
05:05:39.834000 * About to connect() to 192.168.1.101 port 21 (#0)
05:05:39.834000 * Trying 192.168.1.101... Timed out
05:06:00.834000 * couldn't connect to host
05:06:00.834000 * Closing connection #0
curl: (7) couldn't connect to host
None of the above tests hang. --connect-timeout works in both cases.
curl --max-time effective upper limit down by the winsock TCP stack and as you have already stated controlled by TCP/IP TcpMaxConnectRetransmissions registry parameter.
TCP/IP and NetBT configuration parameters for Windows 2000 or Windows NT (KB120642)
http://support.microsoft.com/kb/120642/EN-US/
TCP/IP and NBT configuration parameters for Windows XP (KB314053)
http://support.microsoft.com/kb/314053/EN-US/
I'll repeat tests on a Windows XP SP3 box this weekend.
-=[Yang]=-
Testing on a Windows XP SP3 box the same binary
http://curl.haxx.se/download/curl-7.19.3-win32-nossl.zip to a system which
sends back a RST packet:
C:\test>curl.exe --version
curl 7.19.3 (i586-pc-mingw32msvc) libcurl/7.19.3 zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file
Features: Largefile libz
C:\test>curl.exe -v --trace-time --connect-timeout 8 ftp://192.168.1.30
01:17:24.265000 * About to connect() to 192.168.1.30 port 21 (#0)
01:17:24.265000 * Trying 192.168.1.30... Connection refused
01:17:25.203000 * couldn't connect to host
01:17:25.203000 * Closing connection #0
curl: (7) couldn't connect to host
C:\test>curl.exe -v --trace-time --max-time 77 ftp://192.168.1.30
01:17:46.515000 * About to connect() to 192.168.1.30 port 21 (#0)
01:17:46.531000 * Trying 192.168.1.30... Connection refused
01:17:47.515000 * couldn't connect to host
01:17:47.515000 * Closing connection #0
curl: (7) couldn't connect to host
Testing on a Windows XP SP3 box the same binary
http://curl.haxx.se/download/curl-7.19.3-win32-nossl.zip to a non-existing
system:
C:\test>curl.exe --version
curl 7.19.3 (i586-pc-mingw32msvc) libcurl/7.19.3 zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file
Features: Largefile libz
C:\test>curl.exe -v --trace-time --connect-timeout 8 ftp://192.168.1.101
01:11:02.140000 * About to connect() to 192.168.1.101 port 21 (#0)
01:11:02.156000 * Trying 192.168.1.101... Timeout
01:11:10.156000 * connect() timed out!
01:11:10.156000 * Closing connection #0
curl: (28) connect() timed out!
C:\test>curl.exe -v --trace-time --max-time 77 ftp://192.168.1.101
01:08:32.078000 * About to connect() to 192.168.1.101 port 21 (#0)
01:08:32.093000 * Trying 192.168.1.101... Timed out
01:08:53.328000 * couldn't connect to host
01:08:53.328000 * Closing connection #0
curl: (7) couldn't connect to host
It doesn't hang either.
I can not reproduce the problem with this binary http://curl.haxx.se/download/curl-7.19.3-win32-nossl.zip
I cannot help any further unless a link to the problematic binary is provided.
-=[Yang]=-
So is this some weirdo firewall, virus checker, magic thing sitting there and messing things up?
Yes it is.
It is our "wonderful" McAfee Antivirus Solution which acts like a proxy for port 21 to be able to scan the ftp-data.
I have disabled it today and the connect-timeout works. I came across some postings over the weekend where others had the same problems with other tools.
Thanks, Yang, for checking it on your system! I have tried the same at the weekend on different computers, too and there I could not reproduce this effect just like you. I really appreciate your efforts, Yang! Thanks for helping and thank you, Daniel, for caring about this stuff!
So I think we can close this "bug report". Sorry for having bothered you!
Would it make sense to add some hints to the documentation of the "connect-timeout" option?
First hint might be, that with win32, a timeout greater than 21 seconds might not make sense because, as already stated and agreed by Yang, win32 only sends three SYNs.
Second hint might be, that with win32 some antivirus products cause the connect-timeout to be useless because they act like a proxy for ftp and accept the connection anyway.
Then maybe at least for others it will not take three days to find out :-)
Thanks for providing all this info back. I'm closing this report now as "invalid" as it wasn't truly a curl bug.
I've added a new "question" to the FAQ about this issuse.