📄 betaftpd_complete.patch
字号:
+- close(fd);++ switch (state) {++ case FTRAN_STATE_NONE:++ return "FTRAN_STATE_NONE";++ case FTRAN_STATE_LISTENING:++ return "FTRAN_STATE_LISTENING";++ case FTRAN_STATE_CONNECTED_NO_CMD:++ return "FTRAN_STATE_CONNECTED_NO_CMD";++ case FTRAN_STATE_LISTENING_GOT_CMD:++ return "FTRAN_STATE_LISTENING_GOT_CMD";++ case FTRAN_STATE_GOTPORT:++ return "FTRAN_STATE_GOTPORT";++ case FTRAN_STATE_CONNECTING:++ return "FTRAN_STATE_CONNECTING";++ case FTRAN_STATE_TRANSFERING:++ return "FTRAN_STATE_TRANSFERING";++ case FTRAN_STATE_DESTROYED:++ return "FTRAN_STATE_DESTROYED";++ default:++ ;++ }++ assert(FALSE);++ return "UNKNOWN";++}++++const char *conn_convert_to_string(enum conn_state cstate)++{++ switch (cstate) {++ case CONN_STATE_INIT:++ return "CONN_STATE_INIT";++ case CONN_STATE_WAIT_USER:++ return "CONN_STATE_WAIT_USER";++ case CONN_STATE_WAIT_PASS:++ return "CONN_STATE_WAIT_PASS";++ case CONN_STATE_READY:++ return "CONN_STATE_READY";++ case CONN_STATE_DESTROYED:++ return "CONN_STATE_DESTROYED";++ default:++ ;++ }++ assert(FALSE);++ return "UNKNOWN";+ }+ + #if 0+@@ -334,6 +331,18 @@+ }+ }+ ++void purge_destroyed_list() {++++ struct list_element *elem = destroyed_list_header->next;++ destroyed_list_header->next = NULL;++ while (elem != NULL)++ {++ struct list_element *next = elem->next;++ free(elem);++ elem=next;++ }++}+++ /*+ * remove_from_linked_list():+ * Removes an element (conn, ftran or dcache) from its linked list,+@@ -343,7 +352,7 @@+ {+ if (elem->prev != NULL) elem->prev->next = elem->next;+ if (elem->next != NULL) elem->next->prev = elem->prev;+- free(elem);++ add_to_linked_list(destroyed_list_header,elem);+ }+ + /*+@@ -360,14 +369,20 @@+ if (c == NULL) return c;+ + c->prev_conn = NULL;++ c->transfer = NULL;++ c->sock = sock;++ c->buf_len = c->rest_pos = 0;++ c->auth = CONN_STATE_INIT;++ conn_GOTO(c,CONN_STATE_INIT);+ c->next_conn = NULL;+ + if (sock != -1) {+- ioctl(sock, FIONBIO, &one);+- if (add_fd(sock, POLLIN) != 0) {++ rn_prepare_fd_for_add(sock,own_pid);++ if (rn_add(&rns,sock,client_eventhandler,c) != 0) {+ /* temp unavail */+ send(sock, "230 Server too busy, please try again later.\r\n", 46, 0);+ close(sock);++ c->sock = ILLEGAL_FD;+ return NULL;+ }+ +@@ -379,9 +394,6 @@+ c->prev_conn = NULL;+ }+ +- c->transfer = NULL;+- c->sock = sock;+- c->buf_len = c->auth = c->rest_pos = 0;+ #if WANT_ASCII+ c->ascii_mode = 0;+ #endif+@@ -429,9 +441,12 @@+ #endif+ f->owner = (struct conn * const)c;+ f->sock = sock;+- f->state = 0;+- f->local_file = -1;+-++ f->state = FTRAN_STATE_NONE;++ ftran_GOTO(f,FTRAN_STATE_NONE); /* For logging */++ f->local_file = f->size = f->block_size = -1; ++ f->pos = 0;++ f->tran_start=0;++ + #if WANT_DCACHE+ f->dir_cache = NULL;+ #endif+@@ -445,12 +460,16 @@+ * Destroy a control connection, remove it from the linked+ * list, and clean up after it.+ */+-void destroy_conn(struct conn * const c)++void _destroy_conn(struct conn * const c)+ {+ if (c == NULL) return;+- del_fd(c->sock);++ if (c->sock == ILLEGAL_FD) return;++ rn_del(&rns,c->sock);++ close(c->sock);++ c->sock = ILLEGAL_FD;+ + destroy_ftran(c->transfer);++ conn_GOTO(c,CONN_STATE_DESTROYED);+ remove_from_linked_list((struct list_element *)c);+ }+ +@@ -467,15 +486,22 @@+ * If you wonder why I check for `defined(SOL_TCP)' and don't+ * provide an alternative, see the comments on init_file_transfer().+ */+-void destroy_ftran(struct ftran * const f)++void _destroy_ftran(struct ftran * const f)+ {+ const unsigned int zero = 0;++ int err;+ + if (f == NULL) return;++ if (f->state == FTRAN_STATE_DESTROYED) return;++++ if (f->sock != ILLEGAL_FD) {+ #if defined(TCP_CORK) && defined(SOL_TCP)+- setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));++ err=setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));+ #endif+- del_fd(f->sock);++ rn_del(&rns,f->sock);++ close(f->sock);++ f->sock = ILLEGAL_FD;++ }+ + #if WANT_DCACHE+ if (f->dir_cache) {+@@ -504,188 +530,255 @@+ + f->owner->transfer = NULL;+ +++ #if WANT_DCACHE+ if (f->dir_cache != NULL) f->dir_cache->use_count--;+ #endif+ ++ ftran_GOTO(f,FTRAN_STATE_DESTROYED);+ remove_from_linked_list((struct list_element *)f);+ }+ + /*+- * 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.++ *client_eventhandler():++ rn_WaitAndDispatch calls this function as the event handler for ++ any event occuring on a client connection. + */+-#if HAVE_POLL+-int process_all_clients(const int num_ac)+-#else+-int process_all_clients(const fd_set * const active_clients, const int num_ac)+-#endif++++int client_eventhandler(rn_pollevent_t * e)+ {+- struct conn *c = NULL, *next = first_conn->next_conn;+- int checked_through = 0;++ int bytes_avail,status,loop_cnt;++ struct conn *c = e->client.data;+ +- /* run through the linked list */+- while (next != NULL && checked_through < num_ac) {+- int bytes_avail;++ DPRINT("c->auth = %s\n",conn_convert_to_string(c->auth));+ +- 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++ if (c->auth == CONN_STATE_DESTROYED) ++ return 0;+ +- checked_through++;++/* Normally we would close the connection if we receive a POLLHUP,++ * but there seems to be a bug in epoll in which it sends spurious POLLHUP++ */+ +- 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;+- }++ if (e->revents & (rn_POLLERR /* | rn_POLLHUP */)) {++ DPRINT("revents %x\n", e->revents);++ destroy_conn(c);++ return 1;++ }+ +- /* overrun = disconnect */+- if (c->buf_len + bytes_avail > 254) {+- if (numeric(c, 503, "Buffer overrun; disconnecting."))+- destroy_conn(c);+- continue;++ loop_cnt = 0;++ /* Loop until EWOULDBLOCK or error */++ while (1) {++ bytes_avail =++ recv(c->sock, c->recv_buf + c->buf_len, 255 - c->buf_len, 0);++++ loop_cnt++;++ DPRINT("loop count = %d, recv returns %d\n",loop_cnt,bytes_avail);++ if (bytes_avail > 0) {++ /* overrun = disconnect */++ if (c->buf_len + bytes_avail > 254) {++ if (numeric(c, 503, "Buffer overrun; disconnecting.")) {++ /* numeric didn't destroy the connection, so we get to. */++ destroy_conn(c);+ }++ /* connection is destroyed by here */++ return 1;++ }++++ /* normal case */++ c->buf_len += bytes_avail;++ status = parse_command(c);+ +- c->buf_len += bytes_avail;+- parse_command(c);++ /* a return of 0 means connection is freed. */++ if (!status) break;+ +- /* FIXME: c could be invalid here, as parse_command can destroy it +- if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {+- destroy_conn(c);+- }+- */++ } else if (bytes_avail < 0) {++ if (errno == EWOULDBLOCK)++ break;++ if (errno != EINTR) {++ DPRINT("errno %d, destroying connection\n", errno);++ destroy_conn(c);++ return 1;++ }++ } else /* 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).++ */++ if (loop_cnt == 1) {++ DPRINT("EOF destroying connection,cnt = %d\n",loop_cnt);++ destroy_conn(c);++ return 1;++ } else++ break;+ }+- return checked_through;++ }++ return 0;+ }+ +-/*+- * 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.++/* ftran_listening_ioready():++ * This eventhandler is instantiated when a PASV command is received. The ++ * state of ftran when this is invoked can be one of FTRAN_STATE_LISTENING++ * or FTRAN_STATE_LISTENING_GOT_CMD depending on whether another command is++ * received before this eventhandler can be called.+ */+-#if HAVE_POLL+-int process_all_sendfiles(const int num_ac)+-#else+-int process_all_sendfiles(fd_set * const active_clients, const int num_ac)+-#endif++++int ftran_listening_ioready(rn_pollevent_t *e)+ {+- 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;++ struct ftran *f = e->client.data;++++ if (f->state == FTRAN_STATE_DESTROYED) ++ return 0;++++ assert((f->state == FTRAN_STATE_LISTENING) || (f->state == FTRAN_STATE_LISTENING_GOT_CMD));++++ assert(f->sock == e->fd);++++ DPRINT("fd %d, state %d\n", f->sock, f->state);++++/* Normally we would close the connection if we receive a POLLHUP,++ * but there seems to be a bug in epoll in which it sends spurious POLLHUP++ */++ if (e->revents & (rn_POLLERR /* | rn_POLLHUP */ )) {++ destroy_ftran(f);++ return 0;++ }+ +-#if HAVE_POLL+- if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) {++ /* We only expect one connect, but looping needs to be done to take care of interrupted system calls */++ for(;;){++ struct sockaddr tempaddr;++ int tempaddr_len = sizeof(tempaddr);++ const unsigned int one = 1;++++ int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr,&tempaddr_len);++ DPRINT("accept returned fd %d\n", tempsock);++++ if (tempsock == -1) {++ if (errno == EWOULDBLOCK) ++ break;++ else if ((errno == EINTR) || (errno == EPROTO) || (errno == ECONNABORTED)) ++ continue;+ destroy_ftran(f);+- continue;+- }+-#endif++ break;++ } ++ /* done accepting */++ rn_del(&rns,f->sock); ++ close(f->sock);++ ++ f->sock = tempsock;+ +- /* 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;++ if (f->state == FTRAN_STATE_LISTENING)++ {++ f->state = FTRAN_STATE_CONNECTED_NO_CMD;++ }++ else if (f->state == FTRAN_STATE_LISTENING_GOT_CMD)++ {++ ftran_GOTO(f,FTRAN_STATE_TRANSFERING);++ init_file_transfer(f); /* this function will fake the first event ++ by calling ftran_data_ioready that might++ destroy ftran++ */++ }++ break;+ }++ ++ assert((f->state == FTRAN_STATE_CONNECTED_NO_CMD) || (f->state == FTRAN_STATE_TRANSFERING) || (f->state == FTRAN_STATE_DESTROYED) || (f->state == FTRAN_STATE_LISTENING) || (f->state == FTRAN_STATE_LISTENING_GOT_CMD)); ++ return 0;++}+ +- checked_through++;++/* ftran_data_ioready():++ * Sends data to client that are ready to receive it,++ * and handle xferlog entries for the files that are finished.++ * ++ */++int ftran_data_ioready(rn_pollevent_t * e)++{++ enum ftran_ioresult transfer_status;++ struct ftran *f = e->client.data;+ +-#if HAVE_POLL+- /* Nothing is needed for the poll() version? */+-#else+- FD_CLR(f->sock, active_clients);+-#endif++ if (f->state == FTRAN_STATE_DESTROYED) ++ return 0;+ +- if (f->state == 2) { /* incoming PASV */+- const unsigned int one = 1;+- const int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr,+- &tempaddr_len);++ DPRINT("fd %d, state %d\n", f->sock, f->state);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -