/*PAGE * * command_list * * Send file list to client. * * Input parameters: * info - corresponding SessionInfo structure * char *fname - File (or directory) to list. * * Output parameters: * NONE */ static void command_list(FTPD_SessionInfo_t *info, char const *fname, int wide) { int s; DIR *dirp = 0; struct dirent *dp = 0; struct stat stat_buf; char buf[FTPD_BUFSIZE]; time_t curTime; int sc = 1; send_reply(info, 150, "Opening ASCII mode data connection for LIST."); s = data_socket(info); if(0 > s) { syslog(LOG_ERR, "ftpd: Error connecting to data socket."); return; } if(fname[0] == '\0') fname = "."; if (0 > stat(fname, &stat_buf)) { snprintf(buf, FTPD_BUFSIZE, "%s: No such file or directory.\r\n", fname); send(s, buf, strlen(buf), 0); } else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(fname)))) { snprintf(buf, FTPD_BUFSIZE, "%s: Can not open directory.\r\n", fname); send(s, buf, strlen(buf), 0); } else { time(&curTime); if(!dirp && *fname) sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf); else { /* FIXME: need "." and ".." only when '-a' option is given */ sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf); sc = sc && send_dirline(s, wide, curTime, fname, (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf); while (sc && (dp = readdir(dirp)) != NULL) sc = sc && send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf); } } if(dirp) closedir(dirp); close_data_socket(info); if(sc) send_reply(info, 226, "Transfer complete."); else send_reply(info, 426, "Connection aborted."); }
/*PAGE * * command_pasv * * Handle FTP PASV command. * Open socket, listen for and accept connection on it. * * Input parameters: * info - corresponding SessionInfo structure * * Output parameters: * info->pasv_socket is set to the descriptor of the data socket */ static void command_pasv(FTPD_SessionInfo_t *info) { int s = -1; int err = 1; close_data_socket(info); s = socket(PF_INET, SOCK_STREAM, 0); if (s < 0) syslog(LOG_ERR, "ftpd: Error creating PASV socket: %s", serr()); else { struct sockaddr_in addr; socklen_t addrLen = sizeof(addr); addr = info->ctrl_addr; addr.sin_port = htons(0); if (0 > bind(s, (struct sockaddr *)&addr, addrLen)) syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr()); else if (0 > listen(s, 1)) syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr()); else if(set_socket_timeout(s, info->idle)) { char buf[FTPD_BUFSIZE]; unsigned char const *ip, *p; getsockname(s, (struct sockaddr *)&addr, &addrLen); ip = (unsigned char const*)&(addr.sin_addr); p = (unsigned char const*)&(addr.sin_port); snprintf(buf, FTPD_BUFSIZE, "Entering passive mode (%u,%u,%u,%u,%u,%u).", ip[0], ip[1], ip[2], ip[3], p[0], p[1]); send_reply(info, 227, buf); info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen); if (0 > info->pasv_socket) syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr()); else { close_socket(s); s = -1; err = 0; } } } if(err) { /* (OSV) The note is from FreeBSD FTPD. * Note: a response of 425 is not mentioned as a possible response to * the PASV command in RFC959. However, it has been blessed as a * legitimate response by Jon Postel in a telephone conversation * with Rick Adams on 25 Jan 89. */ send_reply(info, 425, "Can't open passive connection."); close_socket(s); } }
/*PAGE * * session * * This task handles single session. It is waked up when the FTP daemon gets a * service request from a remote machine. Here, we watch for commands that * will come through the control connection. These commands are then parsed * and executed until the connection is closed, either unintentionally or * intentionally with the "QUIT" command. * * Input parameters: * arg - pointer to corresponding SessionInfo. * * Output parameters: * NONE */ static void session(rtems_task_argument arg) { FTPD_SessionInfo_t *const info = (FTPD_SessionInfo_t *)arg; int chroot_made = 0; rtems_libio_set_private_env(); /* chroot() can fail here because the directory may not exist yet. */ chroot_made = chroot(ftpd_root) == 0; while(1) { rtems_event_set set; rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &set); chroot_made = chroot_made || chroot(ftpd_root) == 0; chdir("/"); errno = 0; send_reply(info, 220, FTPD_SERVER_MESSAGE); while (1) { char buf[FTPD_BUFSIZE]; char *cmd, *opts, *args; if (fgets(buf, FTPD_BUFSIZE, info->ctrl_fp) == NULL) { syslog(LOG_INFO, "ftpd: Connection aborted."); break; } split_command(buf, &cmd, &opts, &args); if (!strcmp("QUIT", cmd)) { send_reply(info, 221, "Goodbye."); break; } else { exec_command(info, cmd, args); } } /* Close connection and put ourselves back into the task pool. */ close_data_socket(info); close_stream(info); task_pool_release(info); } }
/*PAGE * * command_port * * This procedure fills address for data connection given the IP address and * port of the remote machine. * * Input parameters: * info - corresponding SessionInfo structure * args - arguments to the "PORT" command. * * Output parameters: * info->data_addr is set according to arguments of the PORT command. * info->use_default is set to 0 on success, 1 on failure. */ static void command_port(FTPD_SessionInfo_t *info, char const *args) { enum { NUM_FIELDS = 6 }; unsigned int a[NUM_FIELDS]; int n; close_data_socket(info); n = sscanf(args, "%u,%u,%u,%u,%u,%u", a+0, a+1, a+2, a+3, a+4, a+5); if(NUM_FIELDS == n) { int i; union { uint8_t b[NUM_FIELDS]; struct { uint32_t ip; uint16_t port; } u ; } ip_info; for(i = 0; i < NUM_FIELDS; ++i) { if(a[i] > 255) break; ip_info.b[i] = (uint8_t)a[i]; } if(i == NUM_FIELDS) { /* Note: while it contradicts with RFC959, we don't allow PORT command * to specify IP address different than those of the originating client * for the sake of safety. */ if (ip_info.u.ip == info->def_addr.sin_addr.s_addr) { info->data_addr.sin_addr.s_addr = ip_info.u.ip; info->data_addr.sin_port = ip_info.u.port; info->data_addr.sin_family = AF_INET; memset(info->data_addr.sin_zero, 0, sizeof(info->data_addr.sin_zero)); info->use_default = 0; send_reply(info, 200, "PORT command successful."); return; /* success */ } else { send_reply(info, 425, "Address doesn't match peer's IP."); return; } } } send_reply(info, 501, "Syntax error."); }
int dc_close2(int fd) { int res = 0; struct vsp_node *node; int32_t size; #ifdef DC_CALL_TRACE showTraceBack(); #endif /* nothing wrong ... yet */ dc_errno = DEOK; node = delete_vsp_node(fd); if (node == NULL) { /* we have not such file descriptor, so lets give a try to system */ return system_close(fd); } dc_real_fsync( node ); if(node->unsafeWrite) { size = htonl(-1); /* send end of data */ writen(node->dataFd, (char *) &size, sizeof(size), NULL); /* FIXME: error detection missing */ if (get_fin(node) < 0) { dc_debug(DC_ERROR, "dc_close: mover did not FIN the data blocks."); res = -1; } } close_data_socket(node->dataFd); deleteQueue(node->queueID); m_unlock(&node->mux); node_destroy(node); return res; }
int dc_close(int fd) { int res = 0; int tmp; int32_t size; int32_t closemsg[6]; int msglen; struct vsp_node *node; #ifdef DC_CALL_TRACE showTraceBack(); #endif /* nothing wrong ... yet */ dc_errno = DEOK; node = delete_vsp_node(fd); if (node == NULL) { /* we have not such file descriptor, so lets give a try to system */ dc_debug(DC_INFO, "Using system native close for [%d].", fd); return system_close(fd); } if ( node->lcb != NULL ) { dc_lcb_clean( node ); } dc_real_fsync( node ); if(node->unsafeWrite > 1) { size = htonl(-1); /* send end of data */ writen(node->dataFd, (char *) &size, sizeof(size), NULL); /* FIXME: error detection missing */ if (get_fin(node) < 0) { dc_debug(DC_ERROR, "dc_close: mover did not FIN the data blocks."); res = -1; } } if(node->reference == 0 ) { if( (node->sum != NULL) && ( node->sum->isOk == 1 ) ) { closemsg[0] = htonl(20); closemsg[2] = htonl(12); closemsg[3] = htonl(DCAP_DATA_SUM); closemsg[4] = htonl(node->sum->type); closemsg[5] = htonl(node->sum->sum); msglen = 6; dc_debug(DC_INFO, "File checksum is: %u", node->sum->sum); }else{ closemsg[0] = htonl(4); msglen = 2; } closemsg[1] = htonl(IOCMD_CLOSE); /* actual command */ dc_debug(DC_IO, "Sending CLOSE for fd:%d ID:%d.", node->dataFd, node->queueID); check_timeout_envar(); dcap_set_alarm(closeTimeOut > 0 ? closeTimeOut : DCAP_IO_TIMEOUT/4); tmp = sendDataMessage(node, (char *) closemsg, msglen*sizeof(int32_t), ASCII_OK, NULL); /* FIXME: error detection missing */ if( tmp < 0 ) { dc_debug(DC_ERROR, "sendDataMessage failed."); /* ignore close errors if file was open for read */ if(node->flags & O_WRONLY) { res = -1; } if(isIOFailed) { isIOFailed = 0; /* command line dwon */ if(!ping_pong(node)) { /* remove file descriptor from the list of control lines in use */ lockMember(); deleteMemberByValue(node->fd); unlockMember(); pollDelete(node->fd); close_control_socket(node->fd, node->tunnel); } } } dcap_set_alarm(0); deleteQueue(node->queueID); } /* * Even if there is still a reference to the dcap session, * we have to close local socket descriptor. */ close_data_socket(node->dataFd); node_destroy(node); return res; }
/*PAGE * * command_store * * Performs the "STOR" command (receive data from client). * * Input parameters: * info - corresponding SessionInfo structure * char *filename - Destination filename. * * Output parameters: * NONE */ static void command_store(FTPD_SessionInfo_t *info, char const *filename) { int s; int n; unsigned long size = 0; struct rtems_ftpd_hook *usehook = NULL; char buf[FTPD_DATASIZE]; int res = 1; int bare_lfs = 0; int null = 0; typedef ssize_t (*WriteProc)(int, void const*, size_t); WriteProc wrt = &write; if(!can_write()) { send_reply(info, 550, "Access denied."); return; } send_mode_reply(info); s = data_socket(info); if(0 > s) return; null = !strcmp("/dev/null", filename); if (null) { /* File "/dev/null" just throws data away. * FIXME: this is hack. Using `/dev/null' filesystem entry would be * better. */ wrt = &discard; } if (!null && rtems_ftpd_configuration.hooks != NULL) { /* Search our list of hooks to see if we need to do something special. */ struct rtems_ftpd_hook *hook; int i; i = 0; hook = &rtems_ftpd_configuration.hooks[i++]; while (hook->filename != NULL) { if (!strcmp(hook->filename, filename)) { usehook = hook; break; } hook = &rtems_ftpd_configuration.hooks[i++]; } } if (usehook != NULL) { /* * OSV: FIXME: Small buffer could be used and hook routine * called multiple times instead. Alternatively, the support could be * removed entirely in favor of configuring RTEMS pseudo-device with * given name. */ char *bigBufr; size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1; /* * Allocate space for our "file". */ bigBufr = (char *)malloc(filesize); if (bigBufr == NULL) { send_reply(info, 451, "Local resource failure: malloc."); close_data_socket(info); return; } /* * Retrieve the file into our buffer space. */ size = 0; while ((n = recv(s, bigBufr + size, filesize - size, 0)) > 0) { size += n; } if (size >= filesize) { send_reply(info, 451, "File too long: buffer size exceeded."); free(bigBufr); close_data_socket(info); return; } /* * Call our hook. */ res = (usehook->hook_function)(bigBufr, size) == 0; free(bigBufr); if(!res) { send_reply(info, 451, "File processing failed."); close_data_socket(info); return; } } else { /* Data transfer to regular file or /dev/null. */ int fd = 0; if(!null) fd = creat(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (0 > fd) { send_reply(info, 550, "Error creating file."); close_data_socket(info); return; } if(info->xfer_mode == TYPE_I) { while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) { if (wrt(fd, buf, n) != n) { res = 0; break; } } } else if(info->xfer_mode == TYPE_A) { int rest = 0; int pended_cr = 0; while (res && rest == 0 && (n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) { char const* e = buf; char const* b; int i; rest = n; if(pended_cr && *e != '\n') { char const lf = '\r'; pended_cr = 0; if(wrt(fd, &lf, 1) != 1) { res = 0; break; } } do { int count; int sub = 0; b = e; for(i = 0; i < rest; ++i, ++e) { int pcr = pended_cr; pended_cr = 0; if(*e == '\r') { pended_cr = 1; } else if(*e == '\n') { if(pcr) { sub = 2; ++i; ++e; break; } ++bare_lfs; } } if(res == 0) break; count = i - sub - pended_cr; if(count > 0 && wrt(fd, b, count) != count) { res = 0; break; } if(sub == 2 && wrt(fd, e - 1, 1) != 1) res = 0; } while((rest -= i) > 0); } } if (0 > close(fd) || res == 0) { send_reply(info, 452, "Error writing file."); close_data_socket(info); return; } } if (bare_lfs > 0) { snprintf(buf, FTPD_BUFSIZE, "Transfer complete. WARNING! %d bare linefeeds received in ASCII mode.", bare_lfs); send_reply(info, 226, buf); } else send_reply(info, 226, "Transfer complete."); close_data_socket(info); }
/*PAGE * * command_retrieve * * Perform the "RETR" command (send file to client). * * Input parameters: * info - corresponding SessionInfo structure * char *filename - source filename. * * Output parameters: * NONE * */ static void command_retrieve(FTPD_SessionInfo_t *info, char const *filename) { int s = -1; int fd = -1; char buf[FTPD_DATASIZE]; struct stat stat_buf; int res = 0; if(!can_read()) { send_reply(info, 550, "Access denied."); return; } if (0 > (fd = open(filename, O_RDONLY))) { send_reply(info, 550, "Error opening file."); return; } if (fstat(fd, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode)) { if (-1 != fd) close(fd); send_reply(info, 550, "Is a directory."); return; } send_mode_reply(info); s = data_socket(info); if (0 <= s) { int n = -1; if(info->xfer_mode == TYPE_I) { while ((n = read(fd, buf, FTPD_DATASIZE)) > 0) { if(send(s, buf, n, 0) != n) break; } } else if (info->xfer_mode == TYPE_A) { int rest = 0; while (rest == 0 && (n = read(fd, buf, FTPD_DATASIZE)) > 0) { char const* e = buf; char const* b; int i; rest = n; do { char lf = '\0'; b = e; for(i = 0; i < rest; ++i, ++e) { if(*e == '\n') { lf = '\n'; break; } } if(send(s, b, i, 0) != i) break; if(lf == '\n') { if(send(s, "\r\n", 2, 0) != 2) break; ++e; ++i; } } while((rest -= i) > 0); } } if (0 == n) { if (0 == close(fd)) { fd = -1; res = 1; } } } if (-1 != fd) close(fd); if (0 == res) send_reply(info, 451, "File read error."); else send_reply(info, 226, "Transfer complete."); close_data_socket(info); return; }