summaryrefslogtreecommitdiff
path: root/apps/netutils/ftpc/ftpc_transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/netutils/ftpc/ftpc_transfer.c')
-rw-r--r--apps/netutils/ftpc/ftpc_transfer.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/apps/netutils/ftpc/ftpc_transfer.c b/apps/netutils/ftpc/ftpc_transfer.c
new file mode 100644
index 000000000..bce30e302
--- /dev/null
+++ b/apps/netutils/ftpc/ftpc_transfer.c
@@ -0,0 +1,400 @@
+/****************************************************************************
+ * apps/netutils/ftpc/ftpc_transfer.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <poll.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include "ftpc_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ftp_pasvmode
+ *
+ * Description:
+ * Enter passive mode.
+ *
+ * In active mode FTP the client connects from a random port (N>1023) to the
+ * FTP server's command port, port 21. Then, the client starts listening to
+ * port N+1 and sends the FTP command PORT N+1 to the FTP server. The server
+ * will then connect back to the client's specified data port from its local
+ * data port, which is port 20. In passive mode FTP the client initiates
+ * both connections to the server, solving the problem of firewalls filtering
+ * the incoming data port connection to the client from the server. When
+ * opening an FTP connection, the client opens two random ports locally
+ * (N>1023 and N+1). The first port contacts the server on port 21, but
+ * instead of then issuing a PORT command and allowing the server to connect
+ * back to its data port, the client will issue the PASV command. The result
+ * of this is that the server then opens a random unprivileged port (P >
+ * 1023) and sends the PORT P command back to the client. The client then
+ * initiates the connection from port N+1 to port P on the server to transfer
+ * data.
+ *
+ ****************************************************************************/
+
+static int ftp_pasvmode(struct ftpc_session_s *session,
+ uint8_t addrport[6])
+{
+ int tmpap[6];
+ char *ptr;
+ int nscan;
+ int ret;
+ int i;
+
+ /* Does this host support the PASV command */
+
+ if (!FTPC_HAS_PASV(session))
+ {
+ ndbg("Host doesn't support passive mode\n");
+ return ERROR;
+ }
+
+ /* Request passive mode */
+
+ ret = ftpc_cmd(session, "PASV");
+ if (ret < 0 || !ftpc_connected(session))
+ {
+ return ERROR;
+ }
+
+ /* Skip over any leading stuff before address begins */
+
+ ptr = session->reply + 4;
+ while (!isdigit((int)*ptr))
+ {
+ ptr++;
+ }
+
+ /* The response is then 6 integer values: four representing the
+ * IP address and two representing the port number.
+ */
+
+ nscan = sscanf(ptr, "%d,%d,%d,%d,%d,%d",
+ &tmpap[0], &tmpap[1], &tmpap[2],
+ &tmpap[3], &tmpap[4], &tmpap[5]);
+ if (nscan != 6)
+ {
+ ndbg("Error parsing PASV reply: '%s'\n", session->reply);
+ return ERROR;
+ }
+
+ /* Then copy the sscanf'ed values as bytes */
+
+
+ for (i = 0; i < 6; i++)
+ {
+ addrport[i] = (uint8_t)(tmpap[i] & 0xff);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ftpc_xfrinit
+ *
+ * Description:
+ * Perform common transfer setup logic.
+ *
+ ****************************************************************************/
+
+int ftpc_xfrinit(FAR struct ftpc_session_s *session)
+{
+ struct sockaddr_in addr;
+ uint8_t addrport[6];
+ uint8_t *paddr;
+ uint8_t *pport;
+ int ret;
+
+ /* We must be connected to initiate a transfer */
+
+ if (!ftpc_connected(session))
+ {
+ goto errout;
+ }
+
+ /* Initialize the data channel */
+
+ ret = ftpc_sockinit(&session->data);
+ if (ret != OK)
+ {
+ ndbg("ftpc_sockinit() failed: %d\n", errno);
+ goto errout;
+ }
+
+ /* Duplicate the address and connection information of the command channel */
+
+ ftpc_sockcopy(&session->data, &session->cmd);
+
+ /* Should we enter passive mode? */
+
+ if (FTPC_IS_PASSIVE(session))
+ {
+ /* Yes.. going passive. */
+
+ ret = ftp_pasvmode(session, addrport);
+ if (ret != OK)
+ {
+ goto errout_with_data;
+ }
+ }
+
+ /* Configure the data socket */
+
+ ftpc_sockgetsockname(&session->cmd, &addr);
+ memcpy(&addr.sin_addr, addrport, (size_t)4);
+ memcpy(&addr.sin_port, addrport+4, (size_t)2);
+
+ /* Connect the data socket */
+
+ ret = ftpc_sockconnect(&session->data, &addr);
+ if (ret < 0)
+ {
+ ndbg("ftpc_sockconnect() failed: %d\n", errno);
+ goto errout_with_data;
+ }
+ else
+ {
+ /* Wait for the connection to be established */
+
+ ftpc_socklisten(&session->data);
+
+ /* Then send our local data channel address to the server */
+
+ paddr = (uint8_t *)&session->data.laddr.sin_addr;
+ pport = (uint8_t *)&session->data.laddr.sin_port;
+
+ ret = ftpc_cmd(session, "PORT %d,%d,%d,%d,%d,%d",
+ paddr[0], paddr[1], paddr[2],
+ paddr[3], pport[0], pport[1]);
+ if (ret < 0)
+ {
+ ndbg("ftpc_cmd() failed: %d\n", errno);
+ goto errout_with_data;
+ }
+ }
+ return OK;
+
+ errout_with_data:
+ ftpc_sockclose(&session->data);
+ errout:
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: ftpc_xfrreset
+ *
+ * Description:
+ * Reset transfer variables
+ *
+ ****************************************************************************/
+
+void ftpc_xfrreset(struct ftpc_session_s *session)
+{
+ session->size = 0;
+ session->flags &= ~FTPC_XFER_FLAGS;
+ session->rstrsize = 0;
+}
+
+/****************************************************************************
+ * Name: ftpc_xfrmode
+ *
+ * Description:
+ * Select ASCII or Binary transfer mode
+ *
+ ****************************************************************************/
+
+int ftpc_xfrmode(struct ftpc_session_s *session, uint8_t xfrmode)
+{
+ int ret;
+
+ DEBUGASSERT(xfrmode != FTPC_XFRMODE_UNKNOWN);
+ if (session->xfrmode != xfrmode)
+ {
+ ret = ftpc_cmd(session, "TYPE %c", xfrmode == FTPC_XFRMODE_ASCII ? 'A' : 'I');
+ session->xfrmode = xfrmode;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: ftpc_xfrabort
+ *
+ * Description:
+ * Abort a transfer in progress
+ *
+ ****************************************************************************/
+
+int ftpc_xfrabort(FAR struct ftpc_session_s *session, FAR FILE *stream)
+{
+ char buffer[CONFIG_FTP_BUFSIZE];
+ FAR struct pollfd fds;
+ int ret;
+
+ /* Make sure that we are still connected */
+
+ if (!ftpc_connected(session))
+ {
+ return ERROR;
+ }
+
+ /* Check if there is data waiting to be read from the cmd channel */
+
+ fds.fd = session->cmd.sd;
+ fds.events = POLLIN;
+ ret = poll(&fds, 1, 0);
+ if (ret > 0)
+ {
+ /* Read data from command channel */
+
+ nvdbg("Flush cmd channel data\n");
+ while (stream && fread(buffer, 1, CONFIG_FTP_BUFSIZE, stream) > 0);
+ return OK;
+ }
+
+ FTPC_SET_INTERRUPT(session);
+
+ /* Send the Telnet interrupt sequence to abort the transfer:
+ * <IAC IP><IAC DM>ABORT<CR><LF>
+ */
+
+ ndbg("Telnet ABORt sequence\n");
+ ftpc_sockprintf(&session->cmd, "%c%c", TELNET_IAC, TELNET_IP); /* Interrupt process */
+ ftpc_sockprintf(&session->cmd, "%c%c", TELNET_IAC, TELNET_DM); /* Telnet synch signal */
+ ftpc_sockprintf(&session->cmd, "ABOR\r\n"); /* Abort */
+ ftpc_sockflush(&session->cmd);
+
+ /* Read remaining bytes from connection */
+
+ while (stream && fread(buffer, 1, CONFIG_FTP_BUFSIZE, stream) > 0);
+ while(stream && fread(buffer, 1, CONFIG_FTP_BUFSIZE, stream) > 0)
+
+ /* Get the ABORt reply */
+
+ fptc_getreply(session);
+
+ /* Expected replys are: "226 Closing data connection" or
+ * "426 Connection closed; transfer aborted"
+ */
+
+ if (session->code != 226 && session->code != 426)
+ {
+ nvdbg("Expected 226 or 426 reply\n");
+ }
+ else
+ {
+ /* Get the next reply */
+
+ fptc_getreply(session);
+
+ /* Expected replys are: or "225 Data connection open; no transfer in progress"
+ * "226 Closing data connection"
+ */
+
+ if (session->code != 226 && session->code != 225)
+ {
+ nvdbg("Expected 225 or 226 reply\n");
+ }
+ }
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: ftpc_waitdata
+ *
+ * Description:
+ * Wait for dta to be available on the provided stream.
+ *
+ ****************************************************************************/
+
+int ftpc_waitdata(FAR struct ftpc_session_s *session, FAR FILE *stream, bool rdwait)
+{
+ FAR struct pollfd fds;
+ int ret;
+
+ /* Check the stream to see if it has input OR is ready for output */
+
+ fds.fd = fileno(stream);
+ fds.events = rdwait ? POLLIN : POLLOUT;
+ ret = poll(&fds, 1, 10*1000);
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ {
+ return OK;
+ }
+ return ERROR;
+ }
+
+ return ret;
+}
+
+
+