📄 ftpd.c
字号:
/* * process_all_clients(): * Processes all the _control_ connections in active_clients * (normally returned from a select(), there are at max * NUM_AC active connections in the set), sending them * through to the command parser if a command has been * entered. */#if HAVE_POLLint process_all_clients(const int num_ac)#elseint process_all_clients(const fd_set * const active_clients, const int num_ac)#endif{ struct conn *c = NULL, *next = first_conn->next_conn; int checked_through = 0; /* run through the linked list */ while (next != NULL && checked_through < num_ac) { int bytes_avail; c = next; next = c->next_conn;#if HAVE_POLL if ((fds[c->sock].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) == 0) { continue; }#else if (!FD_ISSET(c->sock, active_clients)) { continue; }#endif checked_through++; bytes_avail = recv(c->sock, c->recv_buf + c->buf_len, 255 - c->buf_len, 0); if (bytes_avail <= 0) { /* * select() has already told us there's something about * this socket, so if we get a return value of zero, the * client has closed the socket. If we get a return value * of -1 (error), we close the socket ourselves. * * We do the same for poll(), even though we actually have * bits that tell us what is happening (in case of new * input AND error/hangup at the same time, we do an * explicit check at the bottom of the loop as well). */ destroy_conn(c); continue; } /* overrun = disconnect */ if (c->buf_len + bytes_avail > 254) { numeric(c, 503, "Buffer overrun; disconnecting."); destroy_conn(c); continue; } c->buf_len += bytes_avail; parse_command(c); if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) { destroy_conn(c); } } return checked_through;}/* * process_all_sendfiles(): * Sends data to all clients that are ready to receive it. * Also checks for data connections that are newly-connected, * and handler xferlog entries for the files that are finished. */#if HAVE_POLLint process_all_sendfiles(const int num_ac)#elseint process_all_sendfiles(fd_set * const active_clients, const int num_ac)#endif{ struct ftran *f = NULL, *next = first_ftran->next_ftran; int checked_through = 0; struct sockaddr tempaddr; int tempaddr_len = sizeof(tempaddr); while (next != NULL && checked_through < num_ac) { f = next; next = f->next_ftran;#if HAVE_POLL if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) { destroy_ftran(f); continue; }#endif /* state = 2: incoming PASV, state >3: send file */#if HAVE_POLL if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) {#else if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) {#endif continue; } checked_through++;#if HAVE_POLL /* Nothing is needed for the poll() version? */#else FD_CLR(f->sock, active_clients);#endif if (f->state == 2) { /* incoming PASV */ const unsigned int one = 1; const int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr, &tempaddr_len); del_fd(f->sock); if (tempsock == -1) { destroy_ftran(f); continue; } f->sock = tempsock; ioctl(f->sock, FIONBIO, &one); init_file_transfer(f);#if WANT_UPLOAD if (f->upload) continue;#endif } if (f->state < 5) { init_file_transfer(f);#if WANT_UPLOAD if (f->upload) continue;#endif } /* for download, we send the first packets right away */#if WANT_UPLOAD if (f->upload) { if (do_upload(f)) continue; } else#endif if (do_download(f)) continue; /* do_{upload,download} returned 0, the transfer is complete */ numeric(f->owner, 226, "Transfer complete."); time(&(f->owner->last_transfer));#if WANT_XFERLOG if (!f->dir_listing) { write_xferlog(f); }#endif destroy_ftran(f);#if WANT_FULLSCREEN update_display(first_conn);#endif } return checked_through;}#if WANT_UPLOADint do_upload(struct ftran *f){ char upload_buf[16384]; int size;#if WANT_ASCII /* keep buffer size small in ascii transfers to prevent process stalling while filtering data on slower computers */ /* * This isn't a big problem, since we won't get * packets this big anyway, the biggest I've seen * was 12kB on 100mbit (but that was from a Windows * machine), so I've reduced the buffer from 64 kB * to 16 kB :-) --Steinar */ const int maxlen = (f->ascii_mode == 1) ? 4096 : 16384;#else const int maxlen = 16384;#endif errno = 0; size = recv(f->sock, upload_buf, maxlen, 0); if (size >= 0) { f->pos += size; }#if WANT_ASCII if (size > 0 && f->ascii_mode == 1) { size = ascii_uploadfilter(upload_buf, size); }#endif if (size > 0 && (write(f->local_file, upload_buf, size) == size)) { return 1; } else if (size == -1) { /* don't write xferlog... or? */ numeric(f->owner, 426, strerror(errno)); destroy_ftran(f); return 1; } return 0;} #endifint do_download(struct ftran *f){#if defined(TCP_CORK) && defined(SOL_TCP) unsigned int zero = 0;#endif char *sendfrom_buf; int bytes_to_send; int more_to_send = 0;#if !HAVE_MMAP char buf[MAX_BLOCK_SIZE];#endif#if WANT_ASCII char buf2[MAX_BLOCK_SIZE * 2];#endif int size;#if HAVE_LINUX_SENDFILE /* * We handle the optimal case first, which is sendfile(). * Here we use a rather simplified sending `algorithm', * leaving most of the quirks to the system calls. */ if (sendfile_supported == 1 && f->dir_listing == 0) { int err; size = f->size - f->pos; if (size > f->block_size) size = f->block_size; if (size < 0) size = 0;#ifdef TCP_CORK if (size != f->block_size) { setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero)); } #endif err = sendfile(f->sock, f->local_file, &f->pos, size); return (f->pos < f->size) && (err > -1); }#endif#if HAVE_MMAP size = f->size - f->pos; if (size > f->block_size) size = f->block_size; if (size < 0) size = 0; bytes_to_send = size; sendfrom_buf = f->file_data + f->pos;#else bytes_to_send = read(f->local_file, buf, f->block_size); sendfrom_buf = buf;#endif if (bytes_to_send == f->block_size) more_to_send = 1;#if WANT_ASCII if (f->ascii_mode == 1) { bytes_to_send = ascii_downloadfilter(sendfrom_buf, buf2, bytes_to_send); sendfrom_buf = buf2; }#endif /* WANT_ASCII */#if defined(TCP_CORK) && defined(SOL_TCP) /* if we believe this is the last packet, unset TCP_CORK */ if (more_to_send == 0) { setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero)); }#endif size = send(f->sock, sendfrom_buf, bytes_to_send, 0); if (size < bytes_to_send) more_to_send = 1;#if WANT_ASCII if (f->ascii_mode == 1 && size < bytes_to_send && size > 0) { size = ascii_findlength(sendfrom_buf, size); }#endif#if HAVE_MMAP if (size > 0) f->pos += size;#endif return more_to_send;}#if WANT_XFERLOGvoid write_xferlog(struct ftran *f){ char temp[256]; time_t now = time(NULL); struct tm *t = localtime(&now); if (xferlog == NULL) return; strftime(temp, 256, "%a %b %d %H:%M:%S %Y", t);#if WANT_UPLOAD fprintf(xferlog, "%s %u %s %lu %s b _ %c a %s ftp 0 * \n",#else fprintf(xferlog, "%s %u %s %lu %s b _ o a %s ftp 0 *\n",#endif temp, (int)(difftime(now, f->tran_start)), inet_ntoa(f->sin.sin_addr), f->size, f->filename,#if WANT_UPLOAD (f->upload) ? 'i' : 'o',#endif f->owner->username); fflush(xferlog);#if 0 /* vim needs this to work properly :-( */ )#endif}#endif#if 0/* Reallocate the buggers constantly */void screw_clients(){ struct conn *c = first_conn; int maxloops = MAXCLIENTS; while (c && c->next_conn) { struct conn *temp = malloc(sizeof(*temp)); if (!temp) break; *temp = *(c->next_conn); if (temp->transfer) temp->transfer->owner = temp; memset(c->next_conn, 0, sizeof(struct conn)); free(c->next_conn); temp->prev_conn = c; c->next_conn = temp; c = c->next_conn; maxloops--; assert(maxloops > 0); }}#endif/* * main(): Main function. Does the initialization, and contains * the main server loop. Takes no command-line arguments * (see README for justification). */int main(void){ int server_sock;#if HAVE_POLL /* the sets are declared globally if we use poll() */#else fd_set fds, fds_send;#endif /*setlinebuf(stdout);*/ setvbuf(stdout, (char *)NULL, _IOLBF, 0); signal(SIGPIPE, SIG_IGN); printf("BetaFTPD version %s, Copyright (C) 1999-2000 Steinar H. Gunderson\n", VERSION); puts("BetaFTPD comes with ABSOLUTELY NO WARRANTY; for details see the file"); puts("COPYING. This is free software, and you are welcome to redistribute it"); puts("under certain conditions; again see the file COPYING for details."); puts(""); /* we don't need stdin */ close(0);#if HAVE_POLL { int i; for (i = 0; i < FD_MAX; i++) { fds[i].fd = -1; fds[i].events = 0; } }#else FD_ZERO(&master_fds); FD_ZERO(&master_send_fds);#endif server_sock = create_server_socket();#if WANT_FULLSCREEN printf("%cc", (char)27); /* reset and clear the screen */#endif /* init dummy first connection */ first_conn = alloc_new_conn(-1); first_ftran = alloc_new_ftran(0, NULL);#if WANT_DCACHE first_dcache = alloc_new_dcache();#endif#if WANT_XFERLOG#if WANT_NONROOT#warning No xferlog support for nonroot yet#else /* open xferlog */ xferlog = fopen("/var/log/xferlog", "r+"); if (xferlog == NULL) xferlog = fopen("/usr/adm/xferlog", "r+"); if (xferlog != NULL) { fseek(xferlog, 0L, SEEK_END); }#endif#endif#if WANT_FORK switch (fork()) { case -1: perror("fork()"); puts("fork() failed, exiting"); exit(0); case 0: break; default: puts("BetaFTPD forked into the background"); exit(0); }#else puts("BetaFTPD active");#endif /* set timeout alarm here (after the fork) */ alarm(60); signal(SIGALRM, handle_alarm);#if HAVE_LINUX_SENDFILE /* check that sendfile() is really implemented (same check as configure does) */ { int out_fd = 1, in_fd = 0; off_t offset = 0; size_t size = 1024; errno = 0; sendfile(out_fd, in_fd, &offset, size); if (errno == ENOSYS) sendfile_supported = 0; }#endif for ( ;; ) { int i;#ifndef HAVE_POLL struct timeval timeout;#endif /*screw_clients(); //look for memory errors */#if WANT_FULLSCREEN update_display(first_conn);#endif#if HAVE_POLL i = poll(fds, highest_fds + 1, 60000);#if 0 { int j; for (j=0; j<=highest_fds; j++) { if (fds[j].revents) printf("fds[%d].fd %d, .revents %x\n", j, fds[j].fd, fds[j].revents); } }#endif#else /* reset fds (gets changed by select()) */ fds = master_fds; fds_send = master_send_fds; /* * wait up to 60 secs for any activity */ timeout.tv_sec = 60; timeout.tv_usec = 0; i = select(FD_SETSIZE, &fds, &fds_send, NULL, &timeout);#endif if (i == -1) { if (errno == EBADF) {#if !HAVE_POLL /* don't like this, but we have to */ clear_bad_fds(&server_sock);#endif } else if (errno != EINTR) {#if HAVE_POLL perror("poll()");#else perror("select()");#endif continue; } }#if HAVE_POLL /* fix an invalid server socket */ if (fds[server_sock].revents & POLLERR) { del_fd(server_sock); server_sock = create_server_socket();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -