$ curl -u user:password -l ftp://name-that-resolves-to-link-local-ipv6/ curl: (14) Couldn't interpret the 227-response Response wireshark log: 0000 32 32 37 20 45 6e 74 65 72 69 6e 67 20 50 61 73 227 Entering Pas 0010 73 69 76 65 20 4d 6f 64 65 20 28 66 65 38 30 3a sive Mode (fe80: 0020 3a __ __ __ __ 3a __ __ __ __ 3a __ __ __ __ 3a :____:____:____: 0030 32 63 36 66 25 77 6c 61 6e 30 2c 32 32 33 2c 31 2c6f%wlan0,223,1 0040 36 31 29 0d 0a 61).. I censored a part of my server's IPv6 My guess is the zone ID / scope ID is not parsed $ curl -V curl 7.39.0 (x86_64-unknown-linux-gnu) libcurl/7.39.0 OpenSSL/1.0.1j zlib/1.2.8 libidn/1.29 libssh2/1.4.3 Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API SPNEGO NTLM NTLM_WB SSL libz TLS-SRP
That's correct...
If you take a look at ftp.c at line 1990 which is where it performs that parsing just before the error message you saw is output:
It needs to look for an IPv6 address if that fails, and then use the IPv6 address instead of the parsed IPv4 address afterwards.
Do you fancy having a go at implementing this for us as you have an ideal test bed for it?
Okay I'll see what I can do. Do you accept code via Github PRs?
EDIT: Actually, I'm not convinced this is the right thing to do. According to RFC 2428 the PASV command should have been replaced by EPSV. In my Wireshark log, the EPSV command is actually sent before the PASV command, so the question is why is the PASV command sent at all.
Last edit: Vojtech Kral 2015-01-04
Much appreciated - many thanks.
Ideally we prefer patches to the libcurl mailing list as there is a greater audience there for review.
However, we can also work with patches attached here, but prefer not to use PRs as a) we don't use their merge facility and b) there are a very limited number of us that subscribe to the notifications.
Kind Regards
Steve
Okay, patches to the ML, gotcha. If any - right now I'm trying to figure why a
PASV
was sent after anEPSV
.More info and maybe a fix:
Here's a trace of what happens:
So, apparently, an EPSV response is parsed, but the connection subsequently fails and so curl tries to switch to PASV wich then fails to parse because we're on IPv6 and PASV is not defined for IPv6. (Never mind the server's PASV response - that's non-standard.)
The reason the connection after EPSV fails is on line 1963 in ftp.c - the original IPv6 address is copied but the zoneID/scopeID is not copied anywhere and cannot be resolved.
How to fix this?
Copy the original host name instead of the ip address string. This is very easy - simply replace
conn->ip_addr_str
on the line I mentioned withconn->host.name
. I've tested this and it works. The downside is the hostname has to resolved again - for some reason it was not cached when I tested this even though it's literally the same hostname as for the first connection. Also, I'm not sure this is the right way to fix the thing - is it okay to have the host name resolved again?I would consider this issue Working As Intended. IPv6 link local addresses with scope are valid only on the local machine. No remote FTP server would have the knowledge of the local scope identifier of the machine making an FTP request (except in odd, nonstandard cases). Honoring such an address could also open up a security issue, as it would allow a remote server to cause the client to access an arbitrary server behind a firewall that would not otherwise be accessible. I would argue that a server returning a link local address with scope is an error.
Yes, you're right, the PASV response is definitely wrong (the %wlan0 interface in the response is actually an interface on the server - it's messed up). However the EPSV response not being handled right is a bug, see my comment above. (I can't edit the bug now.)
Diff:
Fix in git now, in commit 99e71e6a847a72b. Thanks a lot for your report!