📄 ftpd.c
字号:
}void nack(const char *s){ reply(502, "%s command not implemented.", s);}/* ARGSUSED */void yyerror(char *s){ char *cp; (void)s; /* ignore argument */ if ((cp = strchr(cbuf,'\n'))!=NULL) *cp = '\0'; reply(500, "'%s': command not understood.", cbuf);}void delete(char *name){ struct stat st; LOGCMD("delete", name); if (stat(name, &st) < 0) { perror_reply(550, name); return; } if ((st.st_mode&S_IFMT) == S_IFDIR) { if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (unlink(name) < 0) { perror_reply(550, name); return; }done: ack("DELE");}void cwd(const char *path){ FILE *message; if (chdir(path) < 0) perror_reply(550, path); else { if ((message = fopen(_PATH_CWDMESG, "r")) != NULL) { char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), message) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(250, "%s", line); } (void) fflush(stdout); (void) fclose(message); } ack("CWD"); }}void replydirname(const char *name, const char *message){ char npath[MAXPATHLEN]; int i; for (i = 0; *name != '\0' && i < (int)sizeof(npath) - 1; i++, name++) { npath[i] = *name; if (*name == '"') npath[++i] = '"'; } npath[i] = '\0'; reply(257, "\"%s\" %s", npath, message);}void makedir(char *name){ LOGCMD("mkdir", name); if (mkdir(name, 0777) < 0) perror_reply(550, name); else replydirname(name, "directory created.");}void removedir(char *name){ LOGCMD("rmdir", name); if (rmdir(name) < 0) perror_reply(550, name); else ack("RMD");}void pwd(void){ char path[MAXPATHLEN]; if (getcwd(path, sizeof path) == (char *)NULL) reply(550, "%s.", path); else replydirname(path, "is current directory.");}char * renamefrom(char *name){ struct stat st; if (stat(name, &st) < 0) { perror_reply(550, name); return ((char *)0); } reply(350, "File exists, ready for destination name"); return (name);}void renamecmd(char *from, char *to){ LOGCMD2("rename", from, to); if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO");}static void dolog(struct sockaddr_in *sn){ struct hostent *hp = gethostbyaddr((char *)&sn->sin_addr, sizeof(struct in_addr), AF_INET); if (hp) (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1); else (void) strncpy(remotehost, inet_ntoa(sn->sin_addr), sizeof(remotehost)-1); remotehost[sizeof(remotehost)-1] = '\0';#ifdef HASSETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); setproctitle("%s", proctitle);#endif /* HASSETPROCTITLE */ if (logging) syslog(LOG_INFO, "connection from %s", remotehost);}/* * Record logout in wtmp file * and exit with supplied status. */void dologout(int status){ sigset_t allsigs; transflag = 0; if (logged_in) { sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); ftpdlogwtmp(ttyline, "", ""); if (doutmp) logout(utmp.ut_line);#if defined(KERBEROS) if (!notickets && krbtkfile_env) unlink(krbtkfile_env);#endif } /* beware of flushing buffers after a SIGPIPE */ _exit(status);}static void myoob(int signo){ char *cp; int save_errno = errno; (void)signo; /* only process if transfer occurring */ if (!transflag) return; cp = tmpline; if (ftpd_getline(cp, 7, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } upper(cp); if (strcmp(cp, "ABOR\r\n") == 0) { tmpline[0] = '\0'; reply(426, "Transfer aborted. Data connection closed."); reply(226, "Abort successful"); longjmp(urgcatch, 1); } if (strcmp(cp, "STAT\r\n") == 0) { if (file_size != (off_t) -1) reply(213, "Status: %qd of %qd bytes transferred", (quad_t) byte_count, (quad_t) file_size); else reply(213, "Status: %qd bytes transferred", (quad_t)byte_count); } errno = save_errno;}/* * 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. */void passive(void){ socklen_t len;#ifdef IP_PORTRANGE int on;#else u_short port;#endif char *p, *a; if (pw == NULL) { reply(530, "Please login with USER and PASS"); return; } if (pdata >= 0) close(pdata); pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; }#ifdef IP_PORTRANGE on = high_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error;#else#define FTP_DATA_BOTTOM 40000#define FTP_DATA_TOP 44999 if (high_data_ports) { for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) { pasv_addr = ctrl_addr; pasv_addr.sin_port = htons(port); if (bind(pdata, (struct sockaddr *) &pasv_addr, sizeof(pasv_addr)) == 0) break; if (errno != EADDRINUSE) goto pasv_error; } if (port > FTP_DATA_TOP) goto pasv_error; } else#endif { pasv_addr = ctrl_addr; pasv_addr.sin_port = 0; if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) goto pasv_error; } len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; a = (char *) &pasv_addr.sin_addr; p = (char *) &pasv_addr.sin_port;#define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return;pasv_error: (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return;}/* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. */static int guniquefd(const char *local, char **nam){ static char new[MAXPATHLEN]; struct stat st; int count, len, fd; char *cp; cp = strrchr(local, '/'); if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, cp ? local : "."); return (-1); } if (cp) *cp = '/'; (void) strncpy(new, local, sizeof(new)-1); new[sizeof(new)-1] = '\0'; len = strlen(new); if (len+2+1 >= (int)sizeof(new)-1) return (-1); cp = new + len; *cp++ = '.'; for (count = 1; count < 100; count++) { (void)snprintf(cp, sizeof(new) - (cp - new), "%d", count); fd = open(new, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1) continue; if (nam) *nam = new; return (fd); } reply(452, "Unique file name cannot be created."); return (-1);}/* * Format and send reply containing system error number. */void perror_reply(int code, const char *string){ reply(code, "%s: %s.", string, strerror(errno));}static const char *onefile[] = { "", 0};void send_file_list(const char *whichf){ struct stat st; DIR *dirp = NULL; struct dirent *dir; FILE *volatile dout = NULL; char const *const *volatile dirlist; const char *dirname; volatile int simple = 0; volatile int freeglob = 0; glob_t gl; /* XXX: should the { go away if __linux__? */ if (strpbrk(whichf, "~{[*?") != NULL) {#ifdef __linux__ /* see popen.c */ int flags = GLOB_NOCHECK;#else int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;#endif memset(&gl, 0, sizeof(gl)); freeglob = 1; if (glob(whichf, flags, 0, &gl)) { reply(550, "not found"); goto out; } else if (gl.gl_pathc == 0) { errno = ENOENT; perror_reply(550, whichf); goto out; } /* The cast is necessary because of bugs in C's type system */ dirlist = (char const *const *) gl.gl_pathv; } else { onefile[0] = whichf; dirlist = onefile; simple = 1; } if (setjmp(urgcatch)) { transflag = 0; goto out; } while ((dirname = *dirlist++)!=NULL) { if (stat(dirname, &st) < 0) { /* * If user typed "ls -l", etc, and the client * used NLST, do what the user meant. */ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) { retrieve("/bin/ls %s", dirname); goto out; } perror_reply(550, whichf); if (dout != NULL) { (void) fclose(dout); transflag = 0; data = -1; pdata = -1; } goto out; } if (S_ISREG(st.st_mode)) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } fprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); byte_count += strlen(dirname) + 1; continue; } else if (!S_ISDIR(st.st_mode)) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[MAXPATHLEN];#ifdef __linux__ if (!strcmp(dir->d_name, ".")) continue; if (!strcmp(dir->d_name, "..")) continue;#else if (dir->d_name[0] == '.' && dir->d_namlen == 1) continue; if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && dir->d_namlen == 2) continue;#endif snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); /* * We have to do a stat to insure it's * not a directory or special file. */ if (simple || (stat(nbuf, &st) == 0 && S_ISREG(st.st_mode))) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else fprintf(dout, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); byte_count += strlen(nbuf) + 1; } } (void) closedir(dirp); } if (dout == NULL) reply(550, "No files found."); else if (ferror(dout) != 0) perror_reply(550, "Data connection"); else reply(226, "Transfer complete."); transflag = 0; if (dout != NULL) (void) fclose(dout); data = -1; pdata = -1;out: if (freeglob) { freeglob = 0; globfree(&gl); }}static void reapchild(int signo){ int save_errno = errno; (void)signo; while (wait3(NULL, WNOHANG, NULL) > 0) ; errno = save_errno;}void logxfer(const char *name, off_t size, time_t start){ char buf[400 + MAXHOSTNAMELEN*4 + MAXPATHLEN*4]; char dir[MAXPATHLEN], path[MAXPATHLEN], rpath[MAXPATHLEN]; char vremotehost[MAXHOSTNAMELEN*4], vpath[MAXPATHLEN*4]; char *vpw; time_t now; if ((statfd >= 0) && (getcwd(dir, sizeof(dir)) != NULL)) { time(&now); vpw = (char *)malloc(strlen((guest) ? guestpw : pw->pw_name)*4+1); if (vpw == NULL) return; snprintf(path, sizeof path, "%s/%s", dir, name); if (realpath(path, rpath) == NULL) { strncpy(rpath, path, sizeof rpath-1); rpath[sizeof rpath-1] = '\0'; } strvis(vpath, rpath, VIS_SAFE|VIS_NOSLASH); strvis(vremotehost, remotehost, VIS_SAFE|VIS_NOSLASH); strvis(vpw, (guest) ? guestpw : pw->pw_name, VIS_SAFE|VIS_NOSLASH); snprintf(buf, sizeof(buf), "%.24s %ld %s %qd %s %c %s %c %c %s ftp %d %s %s\n", ctime(&now), (long)(now - start + (now == start)), vremotehost, (long long) size, vpath, ((type == TYPE_A) ? 'a' : 'b'), "*" /* none yet */, 'o', ((guest) ? 'a' : 'r'), vpw, 0 /* none yet */, ((guest) ? "*" : pw->pw_name), dhostname); write(statfd, buf, strlen(buf)); free(vpw); }}#if defined(TCPWRAPPERS)static int check_host(struct sockaddr_in *sin){ struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET); char *addr = inet_ntoa(sin->sin_addr); if (hp) { if (!hosts_ctl("ftpd", hp->h_name, addr, STRING_UNKNOWN)) { syslog(LOG_NOTICE, "tcpwrappers rejected: %s [%s]", hp->h_name, addr); return (0); } } else { if (!hosts_ctl("ftpd", STRING_UNKNOWN, addr, STRING_UNKNOWN)) { syslog(LOG_NOTICE, "tcpwrappers rejected: [%s]", addr); return (0); } } return (1);}#endif /* TCPWRAPPERS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -