ftpd.c
来自「RTEMS (Real-Time Executive for Multiproc」· C语言 代码 · 共 2,063 行 · 第 1/4 页
C
2,063 行
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 * * send_dirline * * Sends one line of LIST command reply corresponding to single file. * * Input parameters: * s - socket descriptor to send data to * wide - if 0, send only file name. If not 0, send 'stat' info as well in * "ls -l" format. * curTime - current time * path - path to be prepended to what is given by 'add' * add - path to be appended to what is given by 'path', the resulting path * is then passed to 'stat()' routine * name - file name to be reported in output * buf - buffer for temporary data * * Output parameters: * returns 0 on failure, 1 on success * */static intsend_dirline(int s, int wide, time_t curTime, char const* path, char const* add, char const* fname, char* buf){ if(wide) { struct stat stat_buf; int plen = strlen(path); int alen = strlen(add); if(plen == 0) { buf[plen++] = '/'; buf[plen] = '\0'; } else { strcpy(buf, path); if(alen > 0 && buf[plen - 1] != '/') { buf[plen++] = '/'; if(plen >= FTPD_BUFSIZE) return 0; buf[plen] = '\0'; } } if(plen + alen >= FTPD_BUFSIZE) return 0; strcpy(buf + plen, add); if (stat(buf, &stat_buf) == 0) { int len; struct tm bt; time_t tf = stat_buf.st_mtime; enum { SIZE = 80 }; enum { SIX_MONTHS = 365*24*60*60/2 }; char timeBuf[SIZE]; gmtime_r(&tf, &bt); if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS) strftime (timeBuf, SIZE, "%b %d %Y", &bt); else strftime (timeBuf, SIZE, "%b %d %H:%M", &bt); len = snprintf(buf, FTPD_BUFSIZE, "%c%c%c%c%c%c%c%c%c%c 1 %5d %5d %11u %s %s\r\n", (S_ISLNK(stat_buf.st_mode)?('l'): (S_ISDIR(stat_buf.st_mode)?('d'):('-'))), (stat_buf.st_mode & S_IRUSR)?('r'):('-'), (stat_buf.st_mode & S_IWUSR)?('w'):('-'), (stat_buf.st_mode & S_IXUSR)?('x'):('-'), (stat_buf.st_mode & S_IRGRP)?('r'):('-'), (stat_buf.st_mode & S_IWGRP)?('w'):('-'), (stat_buf.st_mode & S_IXGRP)?('x'):('-'), (stat_buf.st_mode & S_IROTH)?('r'):('-'), (stat_buf.st_mode & S_IWOTH)?('w'):('-'), (stat_buf.st_mode & S_IXOTH)?('x'):('-'), (int)stat_buf.st_uid, (int)stat_buf.st_gid, (int)stat_buf.st_size, timeBuf, fname ); if(send(s, buf, len, 0) != len) return 0; } } else { int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname); if(send(s, buf, len, 0) != len) return 0; } return 1;}/*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 voidcommand_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_cwd * * Change current working directory. * * Input parameters: * info - corresponding SessionInfo structure * dir - directory name passed in CWD command * * Output parameters: * NONE * */static voidcommand_cwd(FTPD_SessionInfo_t *info, char *dir){ if(chdir(dir) == 0) send_reply(info, 250, "CWD command successful."); else send_reply(info, 550, "CWD command failed.");}/*PAGE * * command_pwd * * Send current working directory to client. * * Input parameters: * info - corresponding SessionInfo structure * * Output parameters: * NONE */static voidcommand_pwd(FTPD_SessionInfo_t *info){ char buf[FTPD_BUFSIZE]; char const* cwd; errno = 0; buf[0] = '"'; cwd = getcwd(buf + 1, FTPD_BUFSIZE - 4); if(cwd) { int len = strlen(cwd); static char const txt[] = "\" is the current directory."; int size = sizeof(txt); if(len + size + 1 >= FTPD_BUFSIZE) size = FTPD_BUFSIZE - len - 2; memcpy(buf + len + 1, txt, size); buf[len + size] = '\0'; send_reply(info, 250, buf); } else { snprintf(buf, FTPD_BUFSIZE, "Error: %s.", serr()); send_reply(info, 452, buf); }}/*PAGE * * command_mdtm * * Handle FTP MDTM command (send file modification time to client)/ * * Input parameters: * info - corresponding SessionInfo structure * fname - file name passed in MDTM command * * Output parameters: * info->cwd is set to new CWD value. */static voidcommand_mdtm(FTPD_SessionInfo_t *info, char const* fname){ struct stat stbuf; char buf[FTPD_BUFSIZE]; if (0 > stat(fname, &stbuf)) { snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr()); send_reply(info, 550, buf); } else { struct tm *t = gmtime(&stbuf.st_mtime); snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d", 1900 + t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); send_reply(info, 213, buf); }}/*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 voidcommand_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; unsigned8 b[NUM_FIELDS]; for(i = 0; i < NUM_FIELDS; ++i) { if(a[i] > 255) break; b[i] = (unsigned8)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. */ unsigned32 const *ip = (unsigned32 *)b; if(*ip == info->def_addr.sin_addr.s_addr) { info->data_addr.sin_addr.s_addr = *ip; info->data_addr.sin_port = *(unsigned16 *)(b + 4); 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.");}/*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 voidcommand_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; int 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 * * skip_options * * Utility routine to skip options (if any) from input command.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?