/**************************************************************************** * apps/netutils/ftpc/ftpc_transfer.c * * Copyright (C) 2011 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * 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 #include #include #include #include #include #include #include #include #include #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: * ABORT */ 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; }