📄 cmds.c
字号:
if (c->recv_buf[0] == 'A') { c->ascii_mode = 1; numeric(c, 200, "Type is ASCII."); } else if (c->recv_buf[0] == 'I') { c->ascii_mode = 0; numeric(c, 200, "Type is IMAGE."); } else { numeric(c, 504, "Unknown type."); }#else numeric(c, 200, "TYPE ignored (always I)");#endif return 1;}/* * cmd_mode(): Handles the MODE command. Only stream mode is supported. */int cmd_mode(struct conn * const c){ c->recv_buf[0] &= (255-32); /* convert to upper case */ if (c->recv_buf[0] == 'S') { numeric(c, 200, "Mode is STREAM."); } else { numeric(c, 504, "Unknown mode."); } return 1;}/* * cmd_stru(): Handles the STRU command. Only file mode is supported. */int cmd_stru(struct conn * const c){ c->recv_buf[0] &= (255-32); /* convert to upper case */ if (c->recv_buf[0] == 'F') { numeric(c, 200, "Structure is FILE."); } else { numeric(c, 504, "Unknown structure."); } return 1;}/* * cmd_help(): Handle the HELP command. I'm sorry, but I'm unwilling * to use a lot of space to explain the RFCs in such a message, * and BetaFTPD doesn't have any special things that should * be noted anywhere. Thus, this message is close to empty. I * feel that a 5xx entry would have been better, but that is * disallowed. * * As with ACCT, this command is supposed to be executed from * everywhere, so we have to run without setuid. I don't like * it, but at the same time I have to idea what could go * wrong... * * Perhaps I should make this message sound a little less * like an error, since the error code is intended for helpful * messages? :-) */int cmd_help(struct conn * const c){ numeric(c, 414, "Sorry, no detailed help; use standard FTP commands."); return 1;}/* * cmd_quit(): Handles the QUIT command, which shuts down the control * and data sockets. */int cmd_quit(struct conn * const c){ numeric(c, 221, "Have a nice day!"); destroy_conn(c); return 0;}/* * cmd_rein(): Handle the REIN command, which does close to a full reset * of the connection. Much of the code here is intentionally * copied directly from alloc_new_conn() -- perhaps we should * modularize this? */int cmd_rein(struct conn * const c){ destroy_ftran(c->transfer); c->buf_len = c->auth = c->rest_pos = 0; /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */ c->curr_dir[0] = '/';#if WANT_FULLSCREEN c->curr_dir[1] = c->last_cmd[0] = '\0';#else c->curr_dir[1] = '\0';#endif time(&(c->last_transfer)); numeric(c, 220, "BetaFTPD " VERSION " ready."); return 1;}#if DOING_PROFILING/* * cmd_exit(): Handles the EXIT command, my own `extension' to the * FTP protocol... IMPORTANT: Only to be used for profiling * purposes!! (It's needed to get some profiling data out * of the server after compiling it with -pg, since such data * is only written on a clear exit()). Any user (even those * not logged in) can issue an EXIT, and make the server shut * down without clearing any sockets etc. In other words: * Don't use it on a production site. */void cmd_exit(struct conn * const c){ while (first_conn->next_conn) destroy_conn(first_conn->next_conn); exit(0);}#endif/* * parse_command(): * Gets a command from c->recv_buf, determines which command * it is, sets proper effective user-ID and calls the command * handler. Finally, it cleans up. * * To me, this command seems optimizable, but I'm not really * sure where :-) */void parse_command(struct conn *c){ int cmlen; const struct handler *h = handler_table; /* first entry */ if (c == NULL) return; /* strip any leading non-ASCII characters (including CR/LFs) */ while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z') && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) { remove_bytes(c, 1); /* not good */ } /* scan, searching for CR or LF */ cmlen = strcspn(c->recv_buf, "\r\n"); if (cmlen >= c->buf_len) return;#if WANT_FULLSCREEN strncpy(c->last_cmd, c->recv_buf, cmlen); c->last_cmd[cmlen] = 0;#endif do { if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) && (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) { if (c->auth < h->min_auth) { numeric(c, 503, "Please login with USER and PASS."); while (c->recv_buf[0] != '\n') remove_bytes(c, 1); } else { char schar;#if !WANT_NONROOT if (h->do_setuid) { seteuid(c->uid); } else { seteuid(0); }#endif remove_bytes(c, strlen(h->cmd_name)); cmlen -= strlen(h->cmd_name); while (c->recv_buf[0] == ' ') { remove_bytes(c, 1); cmlen--; } schar = c->recv_buf[cmlen]; c->recv_buf[cmlen] = 0; /* result of zero means the connection is freed */ if (h->callback(c)) { c->recv_buf[cmlen] = schar;#if !WANT_NONROOT if (h->do_setuid) seteuid(getuid());#endif remove_bytes(c, cmlen); } } return; } } while ((++h)->callback != NULL); numeric(c, 500, "Sorry, no such command."); remove_bytes(c, cmlen); }/* * prepare_for_transfer(): * Prepares an ftran object for a file transfer, setting * file size, opening sockets etc. * * nonroot notice: prepare_for_transfer() assumes all access * checks are already done. */void prepare_for_transfer(struct ftran *f){#if WANT_NONROOT#warning No nonroot checking for prepare_for_transfer() yet#endif#if HAVE_MMAP /* mmap doesn't make temp files for dir listings */ if (!f->dir_listing) {#endif f->size = lseek(f->local_file, 0, SEEK_END); errno = 0;#if WANT_UPLOAD if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0)#endif lseek(f->local_file, f->owner->rest_pos, SEEK_SET);#if HAVE_MMAP }#endif if (f->state == 1) { /* PASV connection */ f->state = 2; /* waiting */ } else if (f->state == 3) { /* PORT connection */ f->state = 4; connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin)); add_fd(f->sock, POLLOUT); } time(&(f->tran_start));}/* * decode_mode(): * Takes a mode_t argument (from a `struct stat'), and * returns the proper dirlist letter for that type. * * Note: S_IFLNK seems to be broken, or perhaps I just have * missed something (S_IFLNK is always set for all *files* on * my glibc 2.0.111 system). * * The most common cases are put first, for speed :-) */char decode_mode(mode_t mode) { if (S_ISREG(mode)) return '-'; if (S_ISDIR(mode)) return 'd'; if (S_ISLNK(mode)) return 'l'; if (S_ISBLK(mode)) return 'b'; if (S_ISCHR(mode)) return 'c'; if (S_ISSOCK(mode)) return 's'; if (S_ISFIFO(mode)) return 'f'; return '-';}/* * translate_path(): * Take an FTP path, do all neccessary root_dir checks, * change to the correct directory and return the proper * file name to open/stat/whatever. The path returned is * relative to the current directory (NOT absolute). chdir() * in any way will `destroy' this argument. * * Note that `path' will be _changed_, and used as a return pointer * base. Do not attempt to free the result from this function -- * if you need to, free path instead. */char *translate_path(struct conn * const c, char * const path){ char *ptr = NULL; /* chdir to the right dir, then chop it off */ chdir(c->curr_dir); ptr = strrchr(path, '/'); if (ptr != NULL) { char save_char = ptr[0]; ptr[0] = 0; if (do_chdir(c, path) == -1) { return NULL; } ptr[0] = save_char; ptr++; } else { ptr = path; } return ptr;}/* * do_openfile(): * Opens the file PATH with access parameters FLAGS, translating * paths and checking permissions as neccessary. Generally, this * should be used whenever you need an open(). * * The parameters might be a bit confusing. To clarify them a bit: * c: IN/OUT (will be changed) * path: IN (but _will_ be changed) * filename: OUT * flags: IN * check_perm: IN */int do_openfile(struct conn * const c, char * const path, char * const filename, const int flags#if WANT_NONROOT , const int check_permission#endif){ char *ptr; struct stat buf;#if WANT_NONROOT if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) { return -1; }#endif ptr = translate_path(c, c->recv_buf); if (ptr == NULL) return -1;#if WANT_UPLOAD if ((flags & O_CREAT) == 0) {#endif TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2); if (!S_ISREG(buf.st_mode)) { numeric(c, 550, "Not a plain file.", ptr); return -2; }#if WANT_UPLOAD }#endif if (filename != NULL) { /* filename should always be != NULL */ strcpy(filename, ptr); } return open(ptr, flags, 0666);}/* * prepare_for_listing(): * Parse list options, put them back into the list_options * structure lo, and make temporary room for the list. */int prepare_for_listing(struct conn * const c, char ** const ptr, struct list_options * const lo){#if !HAVE_MMAP char *tfname;#endif struct ftran *f = c->transfer; char *tmp; char *optr = NULL, *fptr = NULL; #if WANT_NONROOT char chd[512];#endif#if WANT_NONROOT#warning No nonroot checking for prepare_for_listing() yet#endif if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { numeric(c, 425, "No data connection set up; please use PASV or PORT."); return -1; } /* * A little parameter scanning is required here. There can only * be two parts: the directory name, and any options. We'll find * any options first. */ if (c->recv_buf[0] == '-') { optr = c->recv_buf; } else { optr = strstr(c->recv_buf, " -"); } /* Then see if there are any options to parse. */ if (optr != NULL) { while (*++optr) { switch (*optr & (255-32)) { /* uppercase */ case 'R': /* actually case sensitive... */ lo->recursive = 1; break; case 'L': lo->long_listing = 1; break; case 'F': lo->classify = 1; break; case ' ': fptr = optr + 1; *(optr--) = 0; break; default: break; } } } else { fptr = c->recv_buf; } /* then we chdir to the dir in fptr (if any) */ tmp = fptr ? strrchr(fptr, '/') : NULL; if (tmp != NULL) { tmp[0] = 0; if (do_chdir(c, fptr) == -1) return -1; fptr = tmp + 1; } else { /* current directory */ TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1); } /* if no argument, choose all files */ if (fptr == NULL || fptr[0] == 0) { fptr = "*"; } else { /* we need to check if the last part is a directory (no -d switch) */ struct stat buf; if (stat(fptr, &buf) == 0 && S_ISDIR(buf.st_mode)) { TRAP_ERROR(chdir(fptr) == -1, 550, return -1); fptr = "*"; } } *ptr = fptr;#if WANT_NONROOT getcwd(chd, 512); if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) { numeric(c, 550, "Permission denied"); return -1; }#endif#if !HAVE_MMAP tfname = tempnam(NULL, "ftp");#if WANT_NONROOT if (tfname == NULL) tfname = tempnam("/", "ftp");#endif TRAP_ERROR(tfname == NULL, 550, return -1); strcpy(f->filename, tfname); free(tfname); f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666); TRAP_ERROR(f->local_file == -1, 550, return -1);#endif f->dir_listing = 1;#if WANT_UPLOAD f->upload = 0;#endif return 0;}/* * classify(): Takes a mode_t argument (from `struct stat'), and returns * the parameter to be used in an `ls -F'-style listing. */char classify(const mode_t mode){ if (S_ISREG(mode)) { if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { return '*'; } else { return '\0'; } } if (S_ISDIR(mode)) return '/'; if (S_ISLNK(mode)) return '@'; if (S_ISSOCK(mode)) return '='; if (S_ISFIFO(mode)) return '|'; return '\0'; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -