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_ptrsock_ptrfds, 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_ptrsock_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_ptrssl_ptrssl_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 + * 2005-2006 Zhuang Yuyao + * 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(§ion->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(§ion->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; infds && fds->ufds[i].fd!=fd; i++) + ; + if (infds) { + 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);