Menu

#1468 Cannot connect to FTP on link-local IPv6

closed-fixed
ftp (93) ipv6 (7)
5
2015-02-05
2015-01-03
No
$ 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

Discussion

  • Steve Holme

    Steve Holme - 2015-01-03

    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:

      if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
                      &ip[0], &ip[1], &ip[2], &ip[3],
                      &port[0], &port[1]))
    

    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?

     
  • Vojtech Kral

    Vojtech Kral - 2015-01-04

    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
  • Steve Holme

    Steve Holme - 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

     
  • Vojtech Kral

    Vojtech Kral - 2015-01-04

    Okay, patches to the ML, gotcha. If any - right now I'm trying to figure why a PASV was sent after an EPSV.

     
  • Vojtech Kral

    Vojtech Kral - 2015-01-04

    More info and maybe a fix:

    Here's a trace of what happens:

    $ curl -v -u user:password -l ftp://foobar:33333/
    * Hostname was NOT found in DNS cache
    *   Trying fe80::XXXX:XXXX:XXXX:2c6f...
    * Connected to foobar (fe80::XXXX:XXXX:XXXX:2c6f) port 33333 (#0)
    < 220 Service ready for new user.
    > USER user
    < 331 User name okay, need password for user.
    > PASS password
    < 230 User logged in, proceed.
    > PWD
    < 257 "/bar/baz" is current directory.
    * Entry path is '/bar/baz'
    > EPSV
    * Connect data stream passively
    * ftp_perform ends with SECONDARY: 0
    < 229 Entering Passive Mode (|||45508|)
    * Hostname was NOT found in DNS cache
    *   Trying fe80::XXXX:XXXX:XXXX:2c6f...
    * Immediate connect fail for fe80::XXXX:XXXX:XXXX:2c6f: Invalid argument
    * Failed EPSV attempt. Disabling EPSV
    > PASV
    < 227 Entering Passive Mode (fe80::XXXX:XXXX:XXXX:2c6f%wlan0,223,245)
    * Couldn't interpret the 227-response
    * Closing connection 0
    curl: (14) Couldn't interpret the 227-response
    

    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 with conn->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?

     
  • Dan Fandrich

    Dan Fandrich - 2015-01-04

    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.

     
  • Vojtech Kral

    Vojtech Kral - 2015-01-04

    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.)

     
  • Daniel Stenberg

    Daniel Stenberg - 2015-01-08
    • labels: --> ftp, ipv6
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1,4 +1,3 @@
    -
         $ curl -u user:password -l ftp://name-that-resolves-to-link-local-ipv6/
         curl: (14) Couldn't interpret the 227-response
    
    • status: open --> closed-fixed
    • assigned_to: Daniel Stenberg
     
  • Daniel Stenberg

    Daniel Stenberg - 2015-01-08

    Fix in git now, in commit 99e71e6a847a72b. Thanks a lot for your report!