proper user of STOR command

2019-05-04 18:43发布

I need to get the response messages from an ftp server I'm troubleshooting a connection to, so I am using PHP's ftp_raw function, which allows me to send raw ftp commands to the remote server, and get the response string back. (The built-in PHP ftp commands don't return responses :(

Following this accepted answer, The command I'm sending is

PASV
STOR /local/path/to/file.txt

And the server response is

500 /local/path/to/file.txt: The system cannot find the path specified.

And I'm thinking to myself "Of course, the remote host has no idea of my local file system." My hunch is that I'm opening a socket, specifying a remote file name, and I still have to pipe the data through. But I haven't found anything conclusive in documentation in my searching.

What is the complete set of raw ftp commands to upload a file? At what point, and how, do I actually start sending data to the remote server? Can I use the connection set up from ftp_connect() as the socket?

标签: ftp
2条回答
地球回转人心会变
2楼-- · 2019-05-04 19:30

Disclaimer: The first thing you should know is that RFC959 was written a while after FTP became popular and there is still some broken software based on the lack of specification there was before (and some time after) RFC959 was published. Many older (and more stable) FTP libraries have some special handling for some servers to make sure it works the way you want 99.9% of the time. This is especially more common with handling of extensions to the FTP protocol.

The rest of my answer assumes the server is RFC959 compliant.

Also keep in mind that bypassing your FTP client library's higher-level request/response management means you'll need to re-implement a part of this library yourself. This means you should be comfortable with the specification since you'll need to refer to it. Where possible, I'll refer to the appropriate sections so that you can get around.

In an case, I strongly recommend that you debug your problems by stepping into PHP's FTP client library rather than implementing all of this yourself. It it's possible, you should really request that the library output all the commands it's using. All that being said, I will still guide you through the procedure to help diagnose your problem.


Managing the FTP data connection is somewhat of a pain. It's not as easy as it looks at first glance if you want to support all optional parts of the specification. Exactly how you transfer files mostly depends on the current state of the following options:

  1. the data type (section 3.1.1): transferring files is usually safest and most efficient using the image/binary data type. This is not the default and some FTP commands (such as directory listings) require setting it to ASCII, so make sure you always set it before a transfer.
  2. the data structure (section 3.1.2): the file structure is usually what you want, but some older computers and mainframes might have to convert to and from this mode.
  3. the transmission mode (section 3.4): the stream mode is most commonly used, but the block mode supports resuming interrupted transfers and compressed mode is of little interest.
  4. the connection mode (sections 3.2 and 3.3): either the client or the server may establish the data connection by connecting to the its peer. This must be negotiated using either:
    • default: the client listens on port 20; or
    • a custom port: the client tells the server it's listing on another port; or
    • passive mode: the client asks on which port the server will listen.

Pay attention to the specification because some configurations allow you to keep the data connection open while other may require you to close it (e.g. stream mode). If the data connection is already open, then you don't need to reconnect to the server on each transfer.


All this seems really complicated, but it's just informative. It might come in handy while you're debugging. There are really only two popular ways to transfer files using FTP:

  1. The server connects to the client on a second port and sends the file in the image (binary) data type using the file data structure.

    1. Configure the data type (required):

      TYPE I

    2. Configure the data structure (optional, default):

      STRU F

    3. Configure the transfer mode (optional, default):

      MODE S

    4. Choose an available port (this may be somewhat more subtle than you think) and start listening. If you choose the default port (20), skip the next step.

      choose port, create socket, listen on selected port.

    5. Tell the server we'll be listening on a given port (optional if default port is selected, but it doesn't hurt to be careful):

      PORT your-public-ip-address, selected-port

    6. Tell the server to expect a file transfer:

      STOR remote-file-name

    7. Wait for incoming connection from the server.

    8. Send the file contents

      open file, send contents, close file

    9. Close the data connection

      close socket.

  2. The client connects to the server on a second port and sends the file in the image (binary) data type using the file data structure.

    1. Configure the data type (required):

      TYPE I

    2. Configure the data structure (optional, default):

      STRU F

    3. Configure the transfer mode (optional, default):

      MODE S

    4. Tell the server we'll be listening on a given port (required):

      PASV

    5. Read the PASV command response containing IP address and port number the server is listening on.

    6. Tell the server to expect a file transfer:

      STOR remote-file-name

    7. Establish connection:

      connect to server on IP address and port number from PASV response

    8. Send the file contents

      open file, send contents, close file

    9. Close the data connection

      close socket.

There are some inconvenient issues with the first method since choosing a port is a little tricky (after using a port, you need to wait for a short period before using it again) and your firewall or ISP may block incoming connections on some ports, etc. The second method is easiest and should be preferred unless the server rejects it.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-05-04 19:44

Working solution (complete rewrite of earlier solution using PORT). The correct sequence is

PASV
Server responds with something like
"227 Entering Passive Mode (127,0,0,1,30,235)"
STOR /path/on/remote/server/foo.txt
=> Now we have to connect to socket 30*256+235 on 127.0.0.1 and send the data.
Done

Code

$fp = ftp_connect("127.0.0.1", 21, 10) or die("foo");
ftp_login ($fp, "anonymous", "password");
ftp_raw_send_file($fp, "/local/path/to/file.txt", "foo/foo.txt");


function ftp_raw_send_file($fp, $localfile, $remotefile) {

  $connect = ftp_raw($fp, "PASV");

  // parse the response and build the IP and port from the values
  if (count($connect) > 0 && preg_match("/.*\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)/", $connect[0], $m)) {
    $address="{$m[1]}.{$m[2]}.{$m[3]}.{$m[4]}";
    $port=$m[5] * 256 + $m[6];

    print_r(ftp_raw($fp, "STOR $remotefile"));

    $sock = socket_create(AF_INET, SOCK_STREAM, 0);
    if ($sock) {
      socket_connect($sock, $address, $port);
      socket_write($sock, file_get_contents($localfile));
      socket_close($sock);
    }
  }

}
查看更多
登录 后发表回答