📄 ftpd.c
字号:
}#endif /* remove any timed out sockets */ if (time_to_check) { time_out_sockets();#if WANT_DCACHE time_out_dcache();#endif time_to_check = 0; } if (i <= 0) continue;#if HAVE_POLL i -= process_all_sendfiles(i); process_all_clients(i);#else /* sends are given highest `priority' */ i -= process_all_sendfiles(&fds_send, i); /* incoming PASV connections and uploads */ i -= process_all_sendfiles(&fds, i); /* * check the incoming PASV connections first, so * process_all_clients() won't be confused. */ process_all_clients(&fds, i);#endif#if HAVE_POLL if (fds[server_sock].revents & POLLIN) {#else if (FD_ISSET(server_sock, &fds)) {#endif accept_new_client(&server_sock); i--; } }}/* * accept_new_client(): * Open a socket for the new client, say hello and put it in * among the others. */void accept_new_client(int * const server_sock){ struct sockaddr_in tempaddr; int tempaddr_len = sizeof(tempaddr); const int tempsock = accept(*server_sock, (struct sockaddr *)&tempaddr, &tempaddr_len); static int num_err = 0; if (tempsock < 0) {#ifndef WANT_FORK perror("accept()");#endif close(tempsock); if ((errno == EBADF || errno == EPIPE) && ++num_err >= 3) { del_fd(*server_sock); *server_sock = create_server_socket(); } } else { struct conn * const c = alloc_new_conn(tempsock); num_err = 0; if (c != NULL) { numeric(c, 220, "BetaFTPD " VERSION " ready.");#if WANT_STAT memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr));#endif } }}/* * time_out_sockets(): * Times out any socket that has not had any transfer * in the last 15 minutes (delay not customizable by FTP * user -- you must change it in ftpd.h). * * Note that RFC959 explicitly states that there are no * `spontaneous' error replies, yet we have to do it to * get the message through at all. * * If we check this list for every accept() call, it's * actually eating a lot of CPU time, so we only check * it every minute. We used to do a time() call here, * but we've changed to do use an alarm() call and set * the time_to_check_flag in the SIGALRM handler. */RETSIGTYPE handle_alarm(int signum){ time_to_check = 1; alarm(60); /* for libc5 */ signal(SIGALRM, handle_alarm);}void time_out_sockets(){ struct conn *c = NULL, *next = first_conn->next_conn; time_t now = time(NULL); /* run through the linked list */ while (next != NULL) { c = next; next = c->next_conn; if ((c->transfer == NULL || c->transfer->state != 5) && (now - c->last_transfer > TIMEOUT_SECS)) { /* RFC violation? */ numeric(c, 421, "Timeout (%u minutes): Closing control connection.", TIMEOUT_SECS/60); destroy_conn(c); } }}/* * remove_bytes(): * Remove some bytes from the incoming buffer. This gives * room for new data on the control connection, and should * be called when the code has finished using the data. * (This is done automatically for all commands, so you * normally need not worry about it.) */void remove_bytes(struct conn * const c, const int num){ if (c->buf_len <= num) { c->buf_len = 0; } else { c->buf_len -= num; memmove(c->recv_buf, c->recv_buf + num, c->buf_len); }}/* * numeric(): Sends a numeric FTP reply to the client. Note that * you can use this command much the same way as you * would use a printf() (with all the normal %s, %d, * etc.), since it actually uses printf() internally. */void numeric(struct conn * const c, const int numeric, const char * const format, ...){ char buf[256], fmt[256]; va_list args; int i, err; snprintf(fmt, 256, "%03u %s\r\n", numeric, format); va_start(args, format); i = vsnprintf(buf, 256, fmt, args); va_end(args); err = send(c->sock, buf, i, 0); if (err == -1 && errno == EPIPE) { destroy_conn(c); }}/* * init_file_transfer(): * Initiate a data connection for sending. This does not open * any files etc., just does whatever is needed for the socket, * if needed. It does, however, send the 150 reply to the client, * and mmap()s if needed. * * Linux systems (others?) define SOL_TCP right away, which saves us * some grief and code size. Perhaps using getprotoent() is the `right' * way, but it's bigger :-) (Optionally, we could figure it out at * configure time, of course...) * * For optimal speed, we use the Linux 2.2.x-only TCP_CORK flag if * possible. Note that this is only defined in the first `arm' -- * we silently assume that Linux is the only OS supporting this * flag. This might be an over-generalization, but I it looks like * we'll have to depend on it other places as well, so we might * just as well be evil here. */void init_file_transfer(struct ftran * const f){ struct linger ling; struct conn * const c = f->owner; const int mode = IPTOS_THROUGHPUT, zero = 0, one = 1; struct stat buf; int events;#ifdef SOL_TCP /* we want max throughput */ setsockopt(f->sock, SOL_IP, IP_TOS, (void *)&mode, sizeof(mode)); setsockopt(f->sock, SOL_TCP, TCP_NODELAY, (void *)&zero, sizeof(zero));#ifdef TCP_CORK setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&one, sizeof(one));#endif#else /* should these pointers be freed afterwards? */ { getprotoent(); /* legal? */ { const struct protoent * const pe_ip = getprotobyname("ip"); const struct protoent * const pe_tcp = getprotobyname("tcp"); setsockopt(f->sock, pe_ip->p_proto, IP_TOS, (void *)&mode, sizeof(mode)); setsockopt(f->sock, pe_tcp->p_proto, TCP_NODELAY, (void *)&zero, sizeof(zero)); } endprotoent(); }#endif if (f->dir_listing) { f->block_size = MAX_BLOCK_SIZE; } else {#if WANT_ASCII f->ascii_mode = f->owner->ascii_mode;#endif /* find the preferred block size */ f->block_size = MAX_BLOCK_SIZE; if (fstat(f->local_file, &buf) != -1 && buf.st_blksize < MAX_BLOCK_SIZE) { f->block_size = buf.st_blksize; } } f->state = 5; events = POLLOUT;#if WANT_UPLOAD if (f->upload) { events = POLLIN; }#endif /* WANT_UPLOAD */ TRAP_ERROR(add_fd(f->sock, events), 500, return); ling.l_onoff = 0; ling.l_linger = 0; setsockopt(f->sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));#if !HAVE_POLL && WANT_UPLOAD /* * if we let an upload socket stay in master_send_fds, we would * get data that would fool us into closing the socket... (sigh) */ if (f->upload) { FD_CLR(f->sock, &master_send_fds); FD_SET(f->sock, &master_fds); }#endif time(&(f->owner->last_transfer)); if (f->dir_listing) { /* include size? */ numeric(f->owner, 150, "Opening ASCII mode data connection for directory listing."); } else { /* * slightly kludged -- perhaps we should kill the second arm, * at the expense of code size? Or perhaps we could collapse * the two possible replies into one? */#if WANT_ASCII if (f->ascii_mode#if WANT_UPLOAD || f->upload#endif /* WANT_UPLOAD */ ) { numeric(f->owner, 150, "Opening %s mode data connection for '%s'", (f->ascii_mode) ? "ASCII" : "BINARY", f->filename); } else { numeric(f->owner, 150, "Opening %s mode data connection for '%s' (%u bytes)", (f->ascii_mode) ? "ASCII" : "BINARY", f->filename, f->size); }#else /* !WANT_ASCII */#if WANT_UPLOAD if (f->upload) { numeric(f->owner, 150, "Opening BINARY mode data connection for '%s'", f->filename); } else#endif /* WANT_UPLOAD */ numeric(f->owner, 150, "Opening BINARY mode data connection for '%s' (%u bytes)", f->filename, f->size);#endif /* !WANT_ASCII */ } /* * This section _could_ in theory be more optimized, but it's * much easier this way, and hopefully, the compiler will be * intelligent enough to optimize most of this away. The idea * is, some modes _require_ use of mmap (or not). The preferred * thing is using mmap() when we don't have sendfile(), and not * using mmap() when we have sendfile(). */#if HAVE_MMAP if (f->dir_listing == 0) {#if HAVE_LINUX_SENDFILE int do_mmap = (sendfile_supported) ? 0 : 1;#else int do_mmap = 1;#endif#if WANT_ASCII if (f->ascii_mode == 1) do_mmap = 1;#endif#if WANT_UPLOAD if (f->upload == 1) do_mmap = 0;#endif if (do_mmap == 1) { f->file_data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->local_file, 0); if (f->file_data == MAP_FAILED) f->file_data = NULL; } else { f->file_data = NULL; } f->pos = f->owner->rest_pos; }#else /* !HAVE_MMAP */ lseek(f->local_file, f->owner->rest_pos, SEEK_SET);#endif}/* * create_server_socket(): * Create and bind a server socket, that we can use to * listen to new clients on. */int create_server_socket(){ int server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); const unsigned int one = 1; struct sockaddr_in addr; int err; /* * In the `perfect' world, if an address was in use, we could * just wait for the kernel to clear everything up, and everybody * would be happy. But when you just found out your server socket * was invalid, it has to be `re-made', and 3000 users are trying * to access your fileserver, I think it's nice that it comes * up right away... hence this option. */ setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); ioctl(server_sock, FIONBIO, &one); /* just in case */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(FTP_PORT); do { err = bind(server_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)); if (err == -1) { perror("bind()"); /* try to recover from recoverable errors... */ if (errno == ENOMEM || errno == EADDRINUSE) { puts("Waiting 1 sec before trying again..."); sleep(1); } else { puts("Giving up."); exit(1); } } } while (err == -1); listen(server_sock, 20); err = add_fd(server_sock, POLLIN); if (err) { perror("add_fd"); return -1; } return server_sock;}#if !HAVE_POLL/* * clear_bad_fds(): * Try to find invalid socket descriptors, and clean them. * The methods used are rather UGLY, but I can't think of * any good way of checking e.g. server_sock without * doing anything to it :-( * * poll() is able to do this in a much cleaner way, which * we use if we use poll(). That checking isn't done here, * though. */void clear_bad_fds(int * const server_sock){ { fd_set fds; struct timeval tv = { 0, 0 }; FD_ZERO(&fds); FD_SET(*server_sock, &fds); if (select(*server_sock, &fds, NULL, NULL, &tv) == -1) { FD_CLR(*server_sock, &master_fds); close(*server_sock); *server_sock = create_server_socket(); } } /* could do this (conn, ftran) in any order */ { struct conn *c = NULL, *next = first_conn->next_conn; /* run through the linked list */ while (next != NULL) { char buf[1]; c = next; next = c->next_conn; if (read(c->sock, &buf, 0) == -1 && errno == EBADF) { destroy_conn(c); } } } { struct ftran *f = NULL, *next = first_ftran->next_ftran; while (next != NULL) { char buf[1]; f = next; next = f->next_ftran; if (read(f->sock, &buf, 0) == -1 && errno == EBADF) { destroy_ftran(f); } } } }#endif#if WANT_MESSAGE/* * dump_file(): Dumps a file on the control connection. Used for * welcome messages and the likes. Note that outbuf * is so big, to prevent any crashing from users creating * weird .message files (like 1024 LFs)... The size of * the file is limited to 1024 bytes (by truncation). */void dump_file(struct conn * const c, const int num, const char * const filename){ char buf[1024], outbuf[5121]; char *ptr = outbuf + 4; int i, j = -1; const int dumpfile = open(filename, O_RDONLY); if (dumpfile == -1) return; i = read(dumpfile, buf, 1024); if (i <= 0) { close(dumpfile); return; } sprintf(outbuf, "%03u-", num); while (++j < i) { *ptr++ = buf[j]; if (buf[j] == '\n') { sprintf(ptr, "%03u-", num); ptr += 4; } } *ptr++ = '\n'; send(c->sock, outbuf, ptr - outbuf, 0); close(dumpfile);}/* * list_readme(): * Lists all README file in the current (ie. OS current) * directory, in a 250- message. */void list_readmes(struct conn * const c){ glob_t pglob; const time_t now = time(NULL); int i; if (glob("README*", 0, NULL, &pglob) != 0) return; for (i = 0; i < pglob.gl_pathc; i++) { struct stat buf; char str[256]; char *tm; if (stat(pglob.gl_pathv[i], &buf) == -1) continue; /* remove trailing LF */ tm = ctime(&buf.st_mtime); tm[strlen(tm) - 1] = 0; snprintf(str, 256, "250-Please read the file %s\r\n" "250-\tIt was last modified %s - %ld days ago\r\n", pglob.gl_pathv[i], tm, (now - buf.st_mtime) / 86400); send(c->sock, str, strlen(str), 0); } globfree(&pglob);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -