diff -rNu -X ./x stunnel-4.11/src/client.c stunnel/src/client.c
--- stunnel-4.11/src/client.c	2005-07-02 14:55:58.000000000 +0800
+++ stunnel/src/client.c	2005-08-03 10:37:45.000000000 +0800
@@ -167,6 +167,9 @@
     s_log(LOG_NOTICE,
         "Connection %s: %d bytes sent to SSL, %d bytes sent to socket",
          result ? "reset" : "closed", c->ssl_bytes, c->sock_bytes);
+    if (c->opt->protocol && !strcmp(c->opt->protocol, "ftp-data")) {
+        closesocket(c->opt->fd);
+    }
     return result;
 }
 
@@ -195,6 +198,15 @@
         s_ntop(c->accepting_address, &c->peer_addr.addr[0]);
         c->local_rfd.is_socket=1;
         c->local_wfd.is_socket=1; /* TODO: It's not always true */
+
+        /* get local socket address. added by Zhuang Yuyao (zhuangyy@netease.com) */
+        c->local_address[0] = '\0';
+        memset(&addr.sa, 0, sizeof(SOCKADDR_UNION));
+            addrlen=sizeof(SOCKADDR_UNION);
+        if (0 == getsockname(c->local_rfd.fd, &addr.sa, &addrlen)) {
+            s_ntop(c->local_address, &addr);
+        }
+
         /* It's a socket: lets setup options */
         if(set_socket_options(c->local_rfd.fd, 1)<0)
             return -1;
@@ -373,11 +385,14 @@
     int check_SSL_pending;
     enum {CL_OPEN, CL_INIT, CL_RETRY, CL_CLOSED} ssl_closing=CL_OPEN;
     int watchdog=0; /* a counter to detect an infinite loop */
+    LOCAL_OPTIONS opt;
+    int fd_x;
 
     c->sock_ptr=c->ssl_ptr=0;
     sock_rd=sock_wr=ssl_rd=ssl_wr=1;
     c->sock_bytes=c->ssl_bytes=0;
 
+    fd_x = opt.fd = 0;
     do { /* main loop */
         /* set flag to try and read any buffered SSL data
          * if we made room in the buffer by writing to the socket */
@@ -385,21 +400,33 @@
 
         /****************************** setup c->fds structure */
         s_poll_zero(&c->fds); /* Initialize the structure */
-        if(sock_rd && c->sock_ptr<BUFFSIZE) /* socket input buffer not full*/
+        if(sock_rd && c->sock_ptr<BUFFSIZE) { /* socket input buffer not full*/
             s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0);
+            s_log(LOG_DEBUG, "s_poll_add(), sock input fd: (%d)", c->sock_rfd->fd);
+        }
         if((ssl_rd && c->ssl_ptr<BUFFSIZE) /* SSL input buffer not full */ ||
-                ((c->sock_ptr || ssl_closing==CL_RETRY) && want_rd))
+                ((c->sock_ptr || ssl_closing==CL_RETRY) && want_rd)) {
                 /* want to SSL_write or SSL_shutdown but read from the
                  * underlying socket needed for the SSL protocol */
             s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0);
-        if(c->ssl_ptr) /* SSL input buffer not empty */
+            s_log(LOG_DEBUG, "s_poll_add(), ssl input fd: (%d)", c->ssl_rfd->fd);
+        }
+        if(c->ssl_ptr)  { /* SSL input buffer not empty */
             s_poll_add(&c->fds, c->sock_wfd->fd, 0, 1);
+            s_log(LOG_DEBUG, "s_poll_add(), sock output fd: (%d)", c->sock_wfd->fd);
+        }
         if(c->sock_ptr /* socket input buffer not empty */ ||
                 ssl_closing==CL_INIT /* need to send close_notify */ ||
-                ((c->ssl_ptr<BUFFSIZE || ssl_closing==CL_RETRY) && want_wr))
+                ((c->ssl_ptr<BUFFSIZE || ssl_closing==CL_RETRY) && want_wr)) {
                 /* want to SSL_read or SSL_shutdown but write to the
                  * underlying socket needed for the SSL protocol */
+            s_log(LOG_DEBUG, "s_poll_add(), ssl output fd: (%d)", c->ssl_wfd->fd);
             s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1);
+        }
+        if(fd_x>0) {
+            s_log(LOG_DEBUG, "s_poll_add(), child input fd: (%d)", fd_x);
+            s_poll_add(&c->fds, fd_x, 1, 0);
+        }
 
         /****************************** wait for an event */
         err=s_poll_wait(&c->fds, (sock_rd && ssl_rd) /* both peers open */ ||
@@ -419,7 +446,11 @@
                 return 0; /* OK */
             }
         }
-        if(!(sock_can_rd || sock_can_wr || ssl_can_rd || ssl_can_wr)) {
+        if (fd_x>0 && s_poll_canread(&c->fds, fd_x)) {
+            fd_x = 0;
+            accept_connection(&opt);
+        }
+        else if(!(sock_can_rd || sock_can_wr || ssl_can_rd || ssl_can_wr)) {
             s_log(LOG_ERR, "INTERNAL ERROR: "
                 "s_poll_wait returned %d, but no descriptor is ready", err);
             return -1;
@@ -461,6 +492,7 @@
                 c->ssl_ptr-=num;
                 c->sock_bytes+=num;
                 watchdog=0; /* reset watchdog */
+                s_log(LOG_DEBUG, "writesocket: (%d) bytes written to socket (%d).", num, c->sock_wfd->fd);
             }
         }
 
@@ -478,6 +510,7 @@
                 c->sock_ptr-=num;
                 c->ssl_bytes+=num;
                 watchdog=0; /* reset watchdog */
+                s_log(LOG_DEBUG, "SSL_write %d bytes written. fd: (%d)", num, c->ssl_wfd->fd);
                 break;
             case SSL_ERROR_WANT_WRITE:
                 s_log(LOG_DEBUG, "SSL_write returned WANT_WRITE: retrying");
@@ -522,6 +555,7 @@
             default:
                 c->sock_ptr+=num;
                 watchdog=0; /* reset watchdog */
+                s_log(LOG_DEBUG, "readsocket: (%d) bytes read from socket (%d)", num, c->sock_rfd->fd);
             }
         }
 
@@ -539,6 +573,7 @@
             case SSL_ERROR_NONE:
                 c->ssl_ptr+=num;
                 watchdog=0; /* reset watchdog */
+                s_log(LOG_DEBUG, "SSL_read %d bytes read. fd: (%d)", num, c->ssl_rfd->fd);
                 break;
             case SSL_ERROR_WANT_WRITE:
                 s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying");
@@ -577,6 +612,16 @@
             }
         }
 
+        if (c->opt->protocol && !strcmp(c->opt->protocol, "ftp")) {
+#ifdef DEBUG_FTP
+            if (c->sock_ptr > 0) {
+                s_log(LOG_DEBUG, "FTP: server response %s", c->sock_buff);
+            }
+#endif
+            if (0 == ftp_serv_stunnel(c, &opt))
+                fd_x = opt.fd;
+        }
+
         /****************************** check write shutdown conditions */
         if(sock_wr && !ssl_rd && !c->ssl_ptr) {
             s_log(LOG_DEBUG, "Socket write shutdown");
diff -rNu -X ./x stunnel-4.11/src/common.h stunnel/src/common.h
--- stunnel-4.11/src/common.h	2005-08-01 13:55:24.000000000 +0800
+++ stunnel/src/common.h	2005-08-01 14:25:09.000000000 +0800
@@ -265,7 +265,7 @@
 #define STACK_SIZE 65536
 
 /* I/O buffer size */
-#define BUFFSIZE        32768
+#define BUFFSIZE        16384
 
 /* Length of strings (including the terminating '\0' character) */
 #define STRLEN          256
diff -rNu -X ./x stunnel-4.11/src/ftp.c stunnel/src/ftp.c
--- stunnel-4.11/src/ftp.c	1970-01-01 08:00:00.000000000 +0800
+++ stunnel/src/ftp.c	2005-08-03 10:36:13.000000000 +0800
@@ -0,0 +1,268 @@
+/*
+ *   stunnel       Universal SSL tunnel
+ *   Copyright (c) 1998-2005 Michal Trojnara <Michal.Trojnara@mirt.net>
+ *                               2005-2006 Zhuang Yuyao <zhuangyy@xianan.com.cn>
+ *                 All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   In addition, as a special exception, Michal Trojnara gives
+ *   permission to link the code of this program with the OpenSSL
+ *   library (or with modified versions of OpenSSL that use the same
+ *   license as OpenSSL), and distribute linked combinations including
+ *   the two.  You must obey the GNU General Public License in all
+ *   respects for all of the code used other than OpenSSL.  If you modify
+ *   this file, you may extend this exception to your version of the
+ *   file, but you are not obligated to do so.  If you do not wish to
+ *   do so, delete this exception statement from your version.
+ */
+
+#include "common.h"
+#include "prototypes.h"
+
+static int  ftp_pasv2ipport(const char *buf, char *ip, int iplen, unsigned int *port);
+static void ftp_ipport2pasv(char *buf, int len, const char *ip, unsigned int port);
+
+static int  ftp_epsv2port(const char *buf, unsigned int *port);
+static void ftp_port2epsv(const char *buf, int len, unsigned int port);
+
+unsigned int ftp_alloc_port(char* ip, unsigned int port);
+int ftp_init_localoptions(CLI* c, LOCAL_OPTIONS* section,
+                          const char* serv_ip, unsigned int serv_port,
+                          char* local_ip, int local_ip_len, unsigned int* local_port);
+                          
+static int extract_host(const char* address, char* host, int hostlen);
+
+static int ftp_pasv2ipport(const char *buf, char *ip, int iplen, unsigned int *port) {
+    char *ep, *ptr, *ptr2;
+    int i;
+    int num;
+
+    if ( (ptr = strchr(buf, '(')) == NULL)
+        return 1;
+
+    ptr2 = ++ptr;
+    for (i = 0; i<4; i++) {
+        if ((ptr = strchr(++ptr, ',') ) == NULL)
+            return 2;
+        *ptr = '.';
+    }
+    *ptr++ = '\0';
+    strncpy(ip, ptr2, iplen);
+    ptr2 = ptr;
+    if ((ptr = strchr(ptr, ',')) == NULL)
+        return 3;
+    *ptr++ = '\0';
+    num = strtol(ptr2, &ep, 10);
+    if (num < 0 || *ep != '\0')
+        return 4;
+    *port = num * 256;
+    ptr2 = ptr;
+    if ((ptr = strchr(ptr, ')')) == NULL)
+        return 5;
+    *ptr = '\0';
+    num = strtol(ptr2, &ep, 10);
+    if (num < 0 || *ep != '\0')
+        return 6;
+    *port += (unsigned int)num;
+
+    return 0;
+}
+
+static void ftp_ipport2pasv(char *buf, int len, const char *ip, unsigned int port) {
+    char    *ptr;
+    char    tmp[16];
+
+    strncpy(tmp, ip, sizeof(tmp));
+    while ( (ptr = strchr(tmp, '.')) )
+        *ptr = ',';
+    snprintf(buf, len, "227 Entering Passive Mode (%s,%d,%d)\r\n", tmp, port / 256, port % 256);
+}
+
+static int ftp_epsv2port(const char *buf, unsigned int *port) {
+    char *ptr, *ptr2;
+    char *ep;
+    int num;
+
+    ptr = strstr(buf, "(|||");
+    if (ptr == NULL)
+        return 1;
+    ptr += 4;
+    if (strlen(ptr) < 3)
+        return 2;
+    ptr2 = strstr(ptr, "|)");
+    if (ptr2 == NULL)
+        return 3;
+    *ptr2 = '\0';
+    num = strtol(ptr, &ep, 10);
+    if (num < 0 || *ep != '\0')
+        return 4;
+
+    *port = (unsigned int)num;
+    return 0;
+}
+
+static void ftp_port2epsv(const char *buf, int len, unsigned int port) {
+    snprintf(buf, len, "229 Entering Extended Passive Mode (|||%d|)\r\n", port);
+}
+
+unsigned int ftp_alloc_port(char* ip, unsigned int port) {
+    /* XXX need fix */
+    return port + 1;
+}
+
+int ftp_serv_stunnel(CLI *c, LOCAL_OPTIONS* section)
+{
+    unsigned int serv_port, local_port;
+    char serv_ip[IPLEN-6], local_ip[IPLEN-6];
+    int r;
+    char *buf;
+    
+    if (c->sock_ptr > 0 && c->ssl_ptr > 0) {
+        s_log(LOG_ERR, "FTP: internal error (This should not happen).");
+        return -1;
+    }
+    
+    if (c->sock_ptr > 0) {
+        buf = c->sock_buff;
+        
+        if (memcmp(buf, "227 ", 4) == 0) { /* PASV reply detected */
+            s_log(LOG_DEBUG, "FTP: PASV reply detected. (%s)", buf);
+            if ((r = ftp_pasv2ipport(buf, serv_ip, sizeof(serv_ip), &serv_port)) != 0) {
+                s_log(LOG_ERR, "FTP(PASV): parse response failed (%d).", r);
+                return -1;
+            }
+            s_log(LOG_DEBUG, "FTP(PASV): parse response OK. ip: %s port: %u.", serv_ip, serv_port);
+            
+            if (0 != ftp_init_localoptions(c, section, serv_ip, serv_port, local_ip, sizeof(local_ip), &local_port)) {
+                s_log(LOG_ERR, "FTP(PASV): init local options failed.");
+                return -2;
+            }
+            if (0 != init_serv_bind(section)) {
+                s_log(LOG_ERR, "FTP(PASV): bind failed.");
+                return -3;
+            }
+            ftp_ipport2pasv(c->sock_buff, BUFFSIZE, local_ip, local_port);
+            c->sock_ptr = strlen(buf);
+            s_log(LOG_DEBUG, "FTP(PASV): set reply to (%s).", c->sock_buff);
+            return 0;
+        }
+        else if (memcmp(buf, "229 ", 4) == 0) { /* EPSV reply detected */
+            s_log(LOG_ERR, "FTP: EPSV reply detected. (%s)", buf);
+            if ((r = ftp_epsv2port(buf, &serv_port)) != 0) {
+                s_log(LOG_ERR, "FTP(EPSV): parse response failed (%d).", r);
+                return -1;
+            }
+            s_log(LOG_DEBUG, "FTP(EPSV): parse response OK. port: %u.", serv_port);
+            
+            if (0 != extract_host(c->connecting_address, serv_ip, sizeof(serv_ip))) {
+                s_log(LOG_ERR, "FTP(EPSV): cannot get server address. (%s)", c->connecting_address);
+                return -1;
+            }
+            if (0 != ftp_init_localoptions(c, section, serv_ip, serv_port, local_ip, sizeof(local_ip), &local_port)) {
+                s_log(LOG_ERR, "FTP(EPSV): init local options failed.");
+                return -2;
+            }
+            if (0 != init_serv_bind(section)) {
+                s_log(LOG_ERR, "FTP(EPSV): bind failed.");
+                return -3;
+            }
+            ftp_port2epsv(c->sock_buff, BUFFSIZE, local_port);
+            c->sock_ptr = strlen(buf);
+            s_log(LOG_DEBUG, "FTP(EPSV): set reply to (%s).", c->sock_buff);
+            
+            return 0;
+        }
+    }
+    if (c->ssl_ptr > 0) {
+        if (!strncmp(c->ssl_buff, "PBSZ ", 5)) {
+            c->ssl_ptr = 0;
+            strcpy(c->sock_buff, "200 PBSZ=0\r\n");
+            c->sock_ptr = strlen(c->sock_buff);
+        }
+        else if (!strncmp(c->ssl_buff, "PROT ", 5)) {
+            if (!strncmp(c->ssl_buff, "PROT P", 6)) {
+                c->ssl_ptr = 0;
+                strcpy(c->sock_buff, "200 PROT P OK\r\n");
+                c->sock_ptr = strlen(c->sock_buff);
+            }
+            else {
+                c->ssl_ptr = 0;
+                strcpy(c->sock_buff, "534 Unsupported PROT Command. Please Try PROT P\r\n");
+                c->sock_ptr = strlen(c->sock_buff);
+            }
+        }
+    }
+    return 1;
+}
+
+int ftp_init_localoptions(CLI* c, LOCAL_OPTIONS* section,
+                          const char* serv_ip, unsigned int serv_port,
+                          char* local_ip, int local_ip_len, unsigned int* local_port) {
+    char tmp[STRLEN];
+
+    memcpy(section, c->opt, sizeof(LOCAL_OPTIONS));
+
+    /* initial settings */
+    section->fd=0;
+    section->session=NULL;
+    section->next=NULL;
+    section->protocol = "ftp-data";
+
+    /* new servname */
+    safecopy(tmp, c->opt->servname);
+    safeconcat(tmp, "-data");
+    section->servname = strdup(tmp);
+
+    /* new remote address */
+    snprintf(tmp, sizeof(tmp), "%s:%d", serv_ip, serv_port);
+    section->remote_address = strdup(tmp);
+
+    /* new remote_addr */
+    section->remote_addr.num=0;
+    if(!section->option.delayed_lookup &&
+          !name2addrlist(&section->remote_addr, tmp, DEFAULT_LOOPBACK)) {
+        s_log(LOG_NOTICE, "FTP: Cannot resolve '%s' - delaying DNS lookup", tmp);
+        section->option.delayed_lookup=1;
+    }
+
+    /* new local_addr */
+    if (extract_host(c->local_address, local_ip, local_ip_len) != 0) {
+        strncpy(local_ip, DEFAULT_ANY, local_ip_len);
+    }
+    *local_port = ftp_alloc_port(local_ip, serv_port);
+    snprintf(tmp, sizeof(tmp), "%s:%d", local_ip, *local_port);
+    section->local_addr.num=0;
+    if(!name2addrlist(&section->local_addr, tmp, DEFAULT_ANY)) {
+        s_log(LOG_ERR, "FTP: Cannot resolve local address '%s' - operation aborted.", tmp);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int extract_host(const char* address, char* host, int hostlen) {
+    char *ptr;
+    char tmp[STRLEN];
+
+    safecopy(tmp, address);
+    ptr=strrchr(tmp, ':');
+    if (ptr) {
+      *ptr++='\0';
+      strncpy(host, tmp, hostlen);
+      return 0;
+    }
+    return -1;
+}
diff -rNu -X ./x stunnel-4.11/src/network.c stunnel/src/network.c
--- stunnel-4.11/src/network.c	2005-07-02 15:27:38.000000000 +0800
+++ stunnel/src/network.c	2005-08-02 17:38:51.000000000 +0800
@@ -72,6 +72,17 @@
         fds->ufds[i].events|=POLLOUT;
 }
 
+void s_poll_remove(s_poll_set *fds, int fd) {
+    int i;
+    for(i=0; i<fds->nfds && fds->ufds[i].fd!=fd; i++)
+        ;
+    if (i<fds->nfds) {
+        if (i+1 < fds->nfds)
+            memcpy(&fds->ufds[i], &fds->ufds[i+1], sizeof(struct pollfd)*(fds->nfds-i-1));
+        fds->nfds--;
+    }
+}
+
 int s_poll_canread(s_poll_set *fds, int fd) {
     int i;
 
@@ -102,7 +113,7 @@
     short *signal_revents;
     static int max_nfds=0;
     static struct pollfd *ufds=NULL;
-    
+
     time(&now);
     /* count file descriptors */
     min_timeout=-1;
@@ -254,6 +265,16 @@
             retry=1;
         }
     } while(retry || (retval<0 && get_last_socket_error()==EINTR));
+#ifdef DEBUG_POLL
+    for (i = 0; i < fds->nfds; i ++) {
+        s_log(LOG_DEBUG, "FD=%d, (%s%s)->(%s%s)",
+        fds->ufds[i].fd,
+        fds->ufds[i].events & POLLIN ? "IN" : "",
+        fds->ufds[i].events & POLLOUT ? "OUT" : "",
+        fds->ufds[i].revents & POLLIN ? "IN" : "",
+        fds->ufds[i].revents & POLLOUT ? "OUT" : "");
+    }
+#endif
     return retval;
 }
 
diff -rNu -X ./x stunnel-4.11/src/options.c stunnel/src/options.c
--- stunnel-4.11/src/options.c	2005-06-26 04:11:26.000000000 +0800
+++ stunnel/src/options.c	2005-08-02 17:38:51.000000000 +0800
@@ -1508,4 +1508,31 @@
     return 0; /* FAILED */
 }
 
+void local_options_append(LOCAL_OPTIONS* base, LOCAL_OPTIONS* section) {
+    LOCAL_OPTIONS* opt;
+    if (!base || !section) {
+        base = section;
+        return;
+    }
+    for (opt=base->next; opt; opt=opt->next) {
+        if (!opt->next) {
+            opt->next = section;
+            break;
+        }
+    }
+}
+
+void local_options_remove(LOCAL_OPTIONS* base, LOCAL_OPTIONS* section) {
+    LOCAL_OPTIONS* opt;
+    if (!base || !section) {
+        return;
+    }
+    for (opt=base->next; opt; opt=opt->next) {
+        if (opt->next == section) {
+            opt->next = section->next;
+            section->next = NULL;
+            break;
+        }
+    }
+}
 /* End of options.c */
diff -rNu -X ./x stunnel-4.11/src/protocol.c stunnel/src/protocol.c
--- stunnel-4.11/src/protocol.c	2005-04-11 21:45:56.000000000 +0800
+++ stunnel/src/protocol.c	2005-07-29 15:00:54.000000000 +0800
@@ -58,6 +58,8 @@
         retval = options.option.client ? pop3_client(c) : pop3_server(c);
     else if(!strcmp(c->opt->protocol, "nntp"))
         retval = options.option.client ? nntp_client(c) : nntp_server(c);
+    else if(!strcmp(c->opt->protocol, "ftp") || !strcmp(c->opt->protocol, "ftp-data"))
+        retval = 0;
     else {
         s_log(LOG_ERR, "Protocol %s not supported in %s mode",
             c->opt->protocol, options.option.client ? "client" : "server");
diff -rNu -X ./x stunnel-4.11/src/prototypes.h stunnel/src/prototypes.h
--- stunnel-4.11/src/prototypes.h	2005-07-02 15:03:23.000000000 +0800
+++ stunnel/src/prototypes.h	2005-08-03 10:36:13.000000000 +0800
@@ -41,6 +41,34 @@
     u16 num;                        /* how many addresses are used */
 } SOCKADDR_LIST;
 
+/**************************************** Prototypes for network.c */
+
+#define MAX_FD 64
+
+typedef struct {
+#ifdef HAVE_POLL
+    struct pollfd ufds[MAX_FD];
+    unsigned int nfds;
+#else
+    fd_set irfds, iwfds, orfds, owfds;
+    int max;                  
+#endif
+} s_poll_set;
+
+void s_poll_zero(s_poll_set *);
+void s_poll_add(s_poll_set *, int, int, int);
+void s_poll_remove(s_poll_set *, int);
+int s_poll_canread(s_poll_set *, int);
+int s_poll_canwrite(s_poll_set *, int);            
+int s_poll_wait(s_poll_set *, int);
+
+#ifndef USE_WIN32
+int signal_pipe_init(void);
+void exec_status(void);
+#endif        
+int set_socket_options(int, int);
+int alloc_fd(int);
+
 /**************************************** Prototypes for stunnel.c */
 
 extern int num_clients;
@@ -210,33 +238,8 @@
 } SOCK_OPT;
 
 void parse_config(char *, char *);
-
-/**************************************** Prototypes for network.c */
-
-#define MAX_FD 64
-
-typedef struct {
-#ifdef HAVE_POLL
-    struct pollfd ufds[MAX_FD];
-    unsigned int nfds;
-#else
-    fd_set irfds, iwfds, orfds, owfds;
-    int max;
-#endif
-} s_poll_set;
-
-void s_poll_zero(s_poll_set *);
-void s_poll_add(s_poll_set *, int, int, int);
-int s_poll_canread(s_poll_set *, int);
-int s_poll_canwrite(s_poll_set *, int);
-int s_poll_wait(s_poll_set *, int);
-
-#ifndef USE_WIN32
-int signal_pipe_init(void);
-void exec_status(void);
-#endif
-int set_socket_options(int, int);
-int alloc_fd(int);
+void local_options_append(LOCAL_OPTIONS* base, LOCAL_OPTIONS* section);
+void local_options_remove(LOCAL_OPTIONS* base, LOCAL_OPTIONS* section);
 
 /**************************************** Prototypes for client.c */
 
@@ -249,7 +252,7 @@
 
 typedef struct {
     LOCAL_OPTIONS *opt;
-    char accepting_address[IPLEN], connecting_address[IPLEN]; /* text */
+    char accepting_address[IPLEN], connecting_address[IPLEN], local_address[IPLEN]; /* text */
     SOCKADDR_LIST peer_addr; /* Peer address */
     FD local_rfd, local_wfd; /* Read and write local descriptors */
     FD remote_fd; /* Remote descriptor */
@@ -305,7 +308,7 @@
 /**************************************** Prototypes for sthreads.c */
 
 typedef enum {
-    CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION,
+    CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, CRIT_MAINFDS, CRIT_LOCAL_OPTS,
     CRIT_SECTIONS
 } SECTION_CODE;
 
@@ -350,6 +353,15 @@
 extern GETNAMEINFO s_getnameinfo;
 #endif
 
+/**************************************** Prototypes for ftp.c */
+
+int ftp_serv_stunnel(CLI *c, LOCAL_OPTIONS* opt);
+
+/**************************************** Additional prototypes for stunnel.c */
+
+int init_serv_bind(LOCAL_OPTIONS *);
+void accept_connection(LOCAL_OPTIONS *);
+
 #endif /* defined PROTOTYPES_H */
 
 /* End of prototypes.h */
diff -rNu -X ./x stunnel-4.11/src/stunnel.c stunnel/src/stunnel.c
--- stunnel-4.11/src/stunnel.c	2005-07-10 05:35:38.000000000 +0800
+++ stunnel/src/stunnel.c	2005-08-03 10:36:13.000000000 +0800
@@ -38,7 +38,6 @@
 
     /* Prototypes */
 static void daemon_loop(void);
-static void accept_connection(LOCAL_OPTIONS *);
 static void get_limits(void); /* setup global max_clients and max_fds */
 #if !defined (USE_WIN32) && !defined (__vms)
 static void drop_privileges(void);
@@ -121,9 +120,9 @@
 }
 
 static void daemon_loop(void) {
-    SOCKADDR_UNION addr;
-    s_poll_set fds;
     LOCAL_OPTIONS *opt;
+    s_poll_set fds;
+    int r;
 
     get_limits();
     s_poll_zero(&fds);
@@ -140,32 +139,9 @@
 
     /* bind local ports */
     for(opt=local_options.next; opt; opt=opt->next) {
-        if(!opt->option.accept) /* no need to bind this service */
-            continue;
-        memcpy(&addr, &opt->local_addr.addr[0], sizeof(SOCKADDR_UNION));
-        if((opt->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) {
-            sockerror("local socket");
-            exit(1);
-        }
-        if(alloc_fd(opt->fd))
-            exit(1);
-        if(set_socket_options(opt->fd, 0)<0)
-            exit(1);
-        s_ntop(opt->local_address, &addr);
-        if(bind(opt->fd, &addr.sa, addr_len(addr))) {
-            s_log(LOG_ERR, "Error binding %s to %s",
-                opt->servname, opt->local_address);
-            sockerror("bind");
-            exit(1);
-        }
-        s_log(LOG_DEBUG, "%s bound to %s", opt->servname, opt->local_address);
-        if(listen(opt->fd, 5)) {
-            sockerror("listen");
-            exit(1);
-        }
-#ifdef FD_CLOEXEC
-        fcntl(opt->fd, F_SETFD, FD_CLOEXEC); /* close socket in child execvp */
-#endif
+        r = init_serv_bind(opt);
+        if (r > 0) continue;
+        if (r < 0) exit(r);
         s_poll_add(&fds, opt->fd, 1, 0);
     }
 
@@ -198,7 +174,40 @@
     s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)");
 }
 
-static void accept_connection(LOCAL_OPTIONS *opt) {
+int init_serv_bind(LOCAL_OPTIONS *opt) {
+    SOCKADDR_UNION addr;
+
+    if(!opt->option.accept) /* no need to bind this service */
+        return 1;
+    memcpy(&addr, &opt->local_addr.addr[0], sizeof(SOCKADDR_UNION));
+    if((opt->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) {
+        sockerror("local socket");
+        return -1;
+    }
+    if(alloc_fd(opt->fd))
+        return -2;
+    if(set_socket_options(opt->fd, 0)<0)
+        return -3;
+    s_ntop(opt->local_address, &addr);
+    if(bind(opt->fd, &addr.sa, addr_len(addr))) {
+        s_log(LOG_ERR, "Error binding %s to %s",
+            opt->servname, opt->local_address);
+        sockerror("bind");
+        return -4;
+    }
+    s_log(LOG_DEBUG, "%s bound to %s", opt->servname, opt->local_address);
+    if(listen(opt->fd, 5)) {
+        sockerror("listen");
+        return -5;
+    }
+#ifdef FD_CLOEXEC
+    fcntl(opt->fd, F_SETFD, FD_CLOEXEC); /* close socket in child execvp */
+#endif
+    
+    return 0;
+}
+
+void accept_connection(LOCAL_OPTIONS *opt) {
     SOCKADDR_UNION addr;
     char from_address[IPLEN];
     int s, addrlen=sizeof(SOCKADDR_UNION);
