/* wait for the result of a non-blocking connect */ static int connect_wait(CLI *c, int fd, int timeout) { int error; socklen_t optlen; s_log(LOG_DEBUG, "connect_wait: waiting %d seconds", timeout); s_poll_zero(&c->fds); s_poll_add(&c->fds, fd, 1, 1); switch(s_poll_wait(&c->fds, timeout)) { case -1: sockerror("connect_wait: s_poll_wait"); return -1; /* error */ case 0: s_log(LOG_INFO, "connect_wait: s_poll_wait timeout"); return -1; /* error */ default: if(s_poll_canread(&c->fds, fd)) { /* just connected socket should not be ready for read */ /* get the resulting error code, now */ optlen=sizeof(error); if(!getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &optlen)) errno=error; if(errno) { /* really an error? */ sockerror("connect_wait: getsockopt"); return -1; /* connection failed */ } } if(s_poll_canwrite(&c->fds, fd)) { s_log(LOG_DEBUG, "connect_wait: connected"); return 0; /* success */ } s_log(LOG_ERR, "connect_wait: unexpected s_poll_wait result"); return -1; /* unexpected result */ } return -1; /* should not be possible */ }
void daemon_loop(void) { SERVICE_OPTIONS *opt; int temporary_lack_of_resources; while(1) { temporary_lack_of_resources=0; if(s_poll_wait(fds, -1, -1)>=0) { if(s_poll_canread(fds, signal_pipe[0])) if(signal_pipe_dispatch()) /* received SIGNAL_TERMINATE */ break; /* terminate daemon_loop */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept && s_poll_canread(fds, opt->fd)) if(accept_connection(opt)) temporary_lack_of_resources=1; } else { log_error(LOG_NOTICE, get_last_socket_error(), "daemon_loop: s_poll_wait"); temporary_lack_of_resources=1; } if(temporary_lack_of_resources) { s_log(LOG_NOTICE, "Accepting new connections suspended for 1 second"); sleep(1); /* to avoid log trashing */ } } }
void s_write(CLI *c, int fd, const void *buf, int len) { /* simulate a blocking write */ u8 *ptr=(u8 *)buf; int num; while(len>0) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 0, 1); /* write */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("s_write: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "s_write: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "s_write: s_poll_wait: unknown result"); longjmp(c->err, 1); /* error */ } num=writesocket(fd, (void *)ptr, len); switch(num) { case -1: /* error */ sockerror("writesocket (s_write)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
void s_read(CLI *c, int fd, void *ptr, int len) { /* simulate a blocking read */ int num; while(len>0) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 1, 0); /* read */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("s_read: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "s_read: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "s_read: s_poll_wait: unknown result"); longjmp(c->err, 1); /* error */ } num=readsocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("readsocket (s_read)"); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "Unexpected socket close (s_read)"); longjmp(c->err, 1); } ptr=(u8 *)ptr+num; len-=num; } }
void read_blocking(CLI *c, int fd, u8 *ptr, int len) { /* simulate a blocking read */ s_poll_set fds; int num; while(len>0) { s_poll_zero(&fds); s_poll_add(&fds, fd, 1, 0); /* read */ switch(s_poll_wait(&fds, c->opt->timeout_busy)) { case -1: sockerror("read_blocking: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "read_blocking: s_poll_wait timeout"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "read_blocking: s_poll_wait unknown result"); longjmp(c->err, 1); /* error */ } num=readsocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("readsocket (read_blocking)"); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "Unexpected socket close (read_blocking)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
void write_blocking(CLI *c, int fd, u8 *ptr, int len) { /* simulate a blocking write */ s_poll_set fds; int num; while(len>0) { s_poll_zero(&fds); s_poll_add(&fds, fd, 0, 1); /* write */ switch(s_poll_wait(&fds, c->opt->timeout_busy)) { case -1: sockerror("write_blocking: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "write_blocking: s_poll_wait timeout"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "write_blocking: s_poll_wait unknown result"); longjmp(c->err, 1); /* error */ } num=writesocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("writesocket (write_blocking)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
char *fd_getline(CLI *c, int fd) { char *line, *tmpline; int ptr=0, allocated=32; line=str_alloc(allocated); for(;;) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 1, 0); /* read */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("fd_getline: s_poll_wait"); str_free(line); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "fd_getline: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); str_free(line); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "fd_getline: s_poll_wait: Unknown result"); str_free(line); longjmp(c->err, 1); /* error */ } if(allocated<ptr+1) { allocated*=2; line=str_realloc(line, allocated); } switch(readsocket(fd, line+ptr, 1)) { case -1: /* error */ sockerror("fd_getline: readsocket"); str_free(line); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "fd_getline: Unexpected socket close"); str_free(line); longjmp(c->err, 1); } if(line[ptr]=='\r') continue; if(line[ptr]=='\n') break; if(line[ptr]=='\0') break; if(++ptr>65536) { /* >64KB --> DoS protection */ s_log(LOG_ERR, "fd_getline: Line too long"); str_free(line); longjmp(c->err, 1); } } line[ptr]='\0'; tmpline=str_dup(line); safestring(tmpline); s_log(LOG_DEBUG, " <- %s", tmpline); str_free(tmpline); return line; }
int s_connect(CLI *c, SOCKADDR_UNION *addr, socklen_t addrlen) { int error; char *dst; dst=s_ntop(addr, addrlen); s_log(LOG_INFO, "s_connect: connecting %s", dst); if(!connect(c->fd, &addr->sa, addrlen)) { s_log(LOG_NOTICE, "s_connect: connected %s", dst); str_free(dst); return 0; /* no error -> success (on some OSes over the loopback) */ } error=get_last_socket_error(); if(error!=S_EINPROGRESS && error!=S_EWOULDBLOCK) { s_log(LOG_ERR, "s_connect: connect %s: %s (%d)", dst, s_strerror(error), error); str_free(dst); return -1; } s_log(LOG_DEBUG, "s_connect: s_poll_wait %s: waiting %d seconds", dst, c->opt->timeout_connect); s_poll_init(c->fds); s_poll_add(c->fds, c->fd, 1, 1); switch(s_poll_wait(c->fds, c->opt->timeout_connect, 0)) { case -1: error=get_last_socket_error(); s_log(LOG_ERR, "s_connect: s_poll_wait %s: %s (%d)", dst, s_strerror(error), error); str_free(dst); return -1; case 0: s_log(LOG_ERR, "s_connect: s_poll_wait %s:" " TIMEOUTconnect exceeded", dst); str_free(dst); return -1; default: error=get_socket_error(c->fd); if(error) { s_log(LOG_ERR, "s_connect: connect %s: %s (%d)", dst, s_strerror(error), error); str_free(dst); return -1; } if(s_poll_canwrite(c->fds, c->fd)) { s_log(LOG_NOTICE, "s_connect: connected %s", dst); str_free(dst); return 0; /* success */ } s_log(LOG_ERR, "s_connect: s_poll_wait %s: internal error", dst); str_free(dst); return -1; } return -1; /* should not be possible */ }
static OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req) { BIO *bio=NULL; OCSP_REQ_CTX *req_ctx=NULL; OCSP_RESPONSE *resp=NULL; int err; /* connect specified OCSP server (responder) */ c->fd=s_socket(c->opt->ocsp_addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket (auth_user)"); if(c->fd<0) goto cleanup; if(connect_blocking(c, &c->opt->ocsp_addr, addr_len(&c->opt->ocsp_addr))) goto cleanup; bio=BIO_new_fd(c->fd, BIO_NOCLOSE); if(!bio) goto cleanup; s_log(LOG_DEBUG, "OCSP: server connected"); /* OCSP protocol communication loop */ req_ctx=OCSP_sendreq_new(bio, c->opt->ocsp_path, req, -1); if(!req_ctx) { sslerror("OCSP: OCSP_sendreq_new"); goto cleanup; } while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) { s_poll_init(c->fds); s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio)); err=s_poll_wait(c->fds, c->opt->timeout_busy, 0); if(err==-1) sockerror("OCSP: s_poll_wait"); if(err==0) s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded"); if(err<=0) goto cleanup; } /* s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); */ /* http://www.mail-archive.com/[email protected]/msg61691.html */ if(!resp) { if(ERR_peek_error()) sslerror("OCSP: OCSP_sendreq_nbio"); else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */ s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error"); } cleanup: if(req_ctx) OCSP_REQ_CTX_free(req_ctx); if(bio) BIO_free_all(bio); if(c->fd>=0) { closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ } return resp; }
static int RFC2487(CLI *c, int fd) { s_poll_zero(&c->fds); s_poll_add(&c->fds, fd, 1, 0); switch(s_poll_wait(&c->fds, 0)) { case 0: /* fd not ready to read */ s_log(LOG_DEBUG, "RFC 2487 detected"); return 1; case 1: /* fd ready to read */ s_log(LOG_DEBUG, "RFC 2487 not detected"); return 0; default: /* -1 */ sockerror("RFC2487 (s_poll_wait)"); return -1; } }
void fdgetline(CLI *c, int fd, char *line) { char logline[STRLEN]; s_poll_set fds; int ptr; for(ptr=0;;) { s_poll_zero(&fds); s_poll_add(&fds, fd, 1, 0); /* read */ switch(s_poll_wait(&fds, c->opt->timeout_busy)) { case -1: sockerror("fdgetline: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "fdgetline: s_poll_wait timeout"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "fdgetline: s_poll_wait unknown result"); longjmp(c->err, 1); /* error */ } switch(readsocket(fd, line+ptr, 1)) { case -1: /* error */ sockerror("readsocket (fdgetline)"); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "Unexpected socket close (fdgetline)"); longjmp(c->err, 1); } if(line[ptr]=='\r') continue; if(line[ptr]=='\n') break; if(!line[ptr]) break; if(++ptr==STRLEN) { s_log(LOG_ERR, "Input line too long"); longjmp(c->err, 1); } } line[ptr]='\0'; safecopy(logline, line); safestring(logline); s_log(LOG_DEBUG, " <- %s", logline); }
void *client(void *arg) { CLI *c=arg; #ifdef DEBUG_STACK_SIZE stack_info(1); /* initialize */ #endif s_log(LOG_DEBUG, "%s started", c->opt->servname); if(alloc_fd(c->local_rfd.fd)) return NULL; if(c->local_wfd.fd!=c->local_rfd.fd) if(alloc_fd(c->local_wfd.fd)) return NULL; #ifndef USE_WIN32 if(c->opt->option.remote && c->opt->option.program) c->local_rfd.fd=c->local_wfd.fd=connect_local(c); /* connect and exec options specified together */ /* spawn local program instead of stdio */ #endif c->remote_fd.fd=-1; c->ssl=NULL; cleanup(c, do_client(c)); #ifdef USE_FORK if(!c->opt->option.remote) /* 'exec' specified */ child_status(); /* null SIGCHLD handler was used */ #else enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ s_log(LOG_DEBUG, "%s finished (%d left)", c->opt->servname, --num_clients); leave_critical_section(CRIT_CLIENTS); #endif free(c); #ifdef DEBUG_STACK_SIZE stack_info(0); /* display computed value */ #endif #ifdef USE_WIN32 _endthread(); #endif #ifdef USE_UCONTEXT s_log(LOG_DEBUG, "Context %ld closed", ready_head->id); s_poll_wait(NULL, 0); /* wait on poll() */ s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context"); #endif return NULL; }
NOEXPORT void smtp_server(CLI *c) { char *line; s_poll_init(c->fds); s_poll_add(c->fds, c->local_rfd.fd, 1, 0); switch(s_poll_wait(c->fds, 0, 200)) { /* wait up to 200ms */ case 0: /* fd not ready to read */ s_log(LOG_DEBUG, "RFC 2487 detected"); break; case 1: /* fd ready to read */ s_log(LOG_DEBUG, "RFC 2487 not detected"); return; /* return if RFC 2487 is not used */ default: /* -1 */ sockerror("RFC2487 (s_poll_wait)"); longjmp(c->err, 1); } line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "220")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } fd_printf(c, c->local_wfd.fd, "%s + stunnel", line); str_free(line); line=fd_getline(c, c->local_rfd.fd); if(!is_prefix(line, "EHLO ")) { s_log(LOG_ERR, "Unknown client EHLO"); str_free(line); longjmp(c->err, 1); } fd_printf(c, c->local_wfd.fd, "250-%s Welcome", line); fd_putline(c, c->local_wfd.fd, "250 STARTTLS"); str_free(line); line=fd_getline(c, c->local_rfd.fd); if(!is_prefix(line, "STARTTLS")) { s_log(LOG_ERR, "STARTTLS expected"); str_free(line); longjmp(c->err, 1); } fd_putline(c, c->local_wfd.fd, "220 Go ahead"); str_free(line); }
void *client_thread(void *arg) { CLI *c=arg; #ifdef DEBUG_STACK_SIZE stack_info(1); /* initialize */ #endif client_main(c); #ifdef DEBUG_STACK_SIZE stack_info(0); /* display computed value */ #endif str_stats(); str_cleanup(); /* s_log() is not allowed after str_cleanup() */ #if defined(USE_WIN32) && !defined(_WIN32_WCE) _endthread(); #endif #ifdef USE_UCONTEXT s_poll_wait(NULL, 0, 0); /* wait on poll() */ #endif return NULL; }
void *client(void *arg) { CLI *c=arg; #ifdef DEBUG_STACK_SIZE stack_info(1); /* initialize */ #endif s_log(LOG_DEBUG, "Service %s started", c->opt->servname); if(c->opt->option.remote && c->opt->option.program) { /* connect and exec options specified together */ /* -> spawn a local program instead of stdio */ while((c->local_rfd.fd=c->local_wfd.fd=connect_local(c))>=0) { run_client(c); if(!c->opt->option.retry) break; sleep(1); /* FIXME: not a good idea in ucontext threading */ str_stats(); str_cleanup(); } } else run_client(c); /* str_free() cannot be used here, because corresponding calloc() is called from a different thread */ free(c); #ifdef DEBUG_STACK_SIZE stack_info(0); /* display computed value */ #endif #ifdef USE_UCONTEXT s_log(LOG_DEBUG, "Context %ld closed", ready_head->id); #endif str_stats(); str_cleanup(); /* s_log() is not allowed after str_cleanup() */ #if defined(USE_WIN32) && !defined(_WIN32_WCE) _endthread(); #endif #ifdef USE_UCONTEXT s_poll_wait(NULL, 0, 0); /* wait on poll() */ #endif return NULL; }
/****************************** transfer data */ static void transfer(CLI *c) { int watchdog=0; /* a counter to detect an infinite loop */ int num, err; /* logical channels (not file descriptors!) open for read or write */ int sock_open_rd=1, sock_open_wr=1, ssl_open_rd=1, ssl_open_wr=1; /* awaited conditions on SSL file descriptors */ int shutdown_wants_read=0, shutdown_wants_write=0; int read_wants_read, read_wants_write=0; int write_wants_read=0, write_wants_write; /* actual conditions on file descriptors */ int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr; c->sock_ptr=c->ssl_ptr=0; do { /* main loop of client data transfer */ /****************************** initialize *_wants_* */ read_wants_read= ssl_open_rd && c->ssl_ptr<BUFFSIZE && !read_wants_write; write_wants_write= ssl_open_wr && c->sock_ptr && !write_wants_read; /****************************** setup c->fds structure */ s_poll_init(&c->fds); /* initialize the structure */ /* for plain socket open data strem = open file descriptor */ /* make sure to add each open socket to receive exceptions! */ if(sock_open_rd) s_poll_add(&c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0); if(sock_open_wr) s_poll_add(&c->fds, c->sock_wfd->fd, 0, c->ssl_ptr); /* for SSL assume that sockets are open if there any pending requests */ if(read_wants_read || write_wants_read || shutdown_wants_read) s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0); if(read_wants_write || write_wants_write || shutdown_wants_write) s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1); /****************************** wait for an event */ err=s_poll_wait(&c->fds, (sock_open_rd && ssl_open_rd) /* both peers open */ || c->ssl_ptr /* data buffered to write to socket */ || c->sock_ptr /* data buffered to write to SSL */ ? c->opt->timeout_idle : c->opt->timeout_close, 0); switch(err) { case -1: sockerror("transfer: s_poll_wait"); longjmp(c->err, 1); case 0: /* timeout */ if((sock_open_rd && ssl_open_rd) || c->ssl_ptr || c->sock_ptr) { s_log(LOG_INFO, "transfer: s_poll_wait:" " TIMEOUTidle exceeded: sending reset"); longjmp(c->err, 1); } else { /* already closing connection */ s_log(LOG_ERR, "transfer: s_poll_wait:" " TIMEOUTclose exceeded: closing"); return; /* OK */ } } /****************************** check for errors on sockets */ err=s_poll_error(&c->fds, c->sock_rfd->fd); if(err) { s_log(LOG_NOTICE, "Error detected on socket (read) file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if(c->sock_wfd->fd != c->sock_rfd->fd) { /* performance optimization */ err=s_poll_error(&c->fds, c->sock_wfd->fd); if(err) { s_log(LOG_NOTICE, "Error detected on socket write file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } err=s_poll_error(&c->fds, c->ssl_rfd->fd); if(err) { s_log(LOG_NOTICE, "Error detected on SSL (read) file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if(c->ssl_wfd->fd != c->ssl_rfd->fd) { /* performance optimization */ err=s_poll_error(&c->fds, c->ssl_wfd->fd); if(err) { s_log(LOG_NOTICE, "Error detected on SSL write file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } /****************************** retrieve results from c->fds */ sock_can_rd=s_poll_canread(&c->fds, c->sock_rfd->fd); sock_can_wr=s_poll_canwrite(&c->fds, c->sock_wfd->fd); ssl_can_rd=s_poll_canread(&c->fds, c->ssl_rfd->fd); ssl_can_wr=s_poll_canwrite(&c->fds, c->ssl_wfd->fd); /****************************** checks for internal failures */ /* please report any internal errors to stunnel-users mailing list */ 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); longjmp(c->err, 1); } /* these checks should no longer be needed */ /* I'm going to remove them soon */ if(!sock_open_rd && sock_can_rd) { err=get_socket_error(c->sock_rfd->fd); if(err) { /* really an error? */ s_log(LOG_ERR, "INTERNAL ERROR: " "Closed socket ready to read: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if(c->ssl_ptr) { /* anything left to write */ s_log(LOG_ERR, "INTERNAL ERROR: " "Closed socket ready to read: sending reset"); longjmp(c->err, 1); } s_log(LOG_ERR, "INTERNAL ERROR: " "Closed socket ready to read: write close"); sock_open_wr=0; /* no further write allowed */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ } /****************************** send SSL close_notify message */ if(shutdown_wants_read || shutdown_wants_write) { shutdown_wants_read=shutdown_wants_write=0; num=SSL_shutdown(c->ssl); /* send close_notify */ if(num<0) /* -1 - not completed */ err=SSL_get_error(c->ssl, num); else /* 0 or 1 - success */ err=SSL_ERROR_NONE; switch(err) { case SSL_ERROR_NONE: /* the shutdown was successfully completed */ s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify"); break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying"); shutdown_wants_write=1; break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying"); shutdown_wants_read=1; break; case SSL_ERROR_SYSCALL: /* socket error */ parse_socket_error(c, "SSL_shutdown"); break; case SSL_ERROR_SSL: /* SSL error */ sslerror("SSL_shutdown"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** read from socket */ if(sock_open_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); switch(num) { case -1: parse_socket_error(c, "readsocket"); break; case 0: /* close */ s_log(LOG_DEBUG, "Socket closed on read"); sock_open_rd=0; break; default: c->sock_ptr+=num; watchdog=0; /* reset watchdog */ } } /****************************** write to socket */ if(sock_open_wr && sock_can_wr) { num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); switch(num) { case -1: /* error */ parse_socket_error(c, "writesocket"); break; case 0: s_log(LOG_DEBUG, "No data written to the socket: retrying"); break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); c->ssl_ptr-=num; c->sock_bytes+=num; watchdog=0; /* reset watchdog */ } } /****************************** update *_wants_* based on new *_ptr */ /* this update is also required for SSL_pending() to be used */ read_wants_read= ssl_open_rd && c->ssl_ptr<BUFFSIZE && !read_wants_write; write_wants_write= ssl_open_wr && c->sock_ptr && !write_wants_read; /****************************** read from SSL */ if((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) || /* it may be possible to read some pending data after * writesocket() above made some room in c->ssl_buff */ (read_wants_write && ssl_can_wr)) { read_wants_write=0; num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: c->ssl_ptr+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); read_wants_write=1; break; case SSL_ERROR_WANT_READ: /* nothing unexpected */ break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_read returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if(!num) { /* EOF */ if(c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed on SSL_read " "with %d byte(s) in buffer", c->sock_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); ssl_open_rd=ssl_open_wr=0; /* buggy peer: no close_notify */ } else parse_socket_error(c, "SSL_read"); break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_read"); ssl_open_rd=0; if(!strcmp(SSL_get_version(c->ssl), "SSLv2")) ssl_open_wr=0; break; case SSL_ERROR_SSL: sslerror("SSL_read"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** write to SSL */ if((write_wants_read && ssl_can_rd) || (write_wants_write && ssl_can_wr)) { write_wants_read=0; num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); c->sock_ptr-=num; c->ssl_bytes+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: /* nothing unexpected */ break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); write_wants_read=1; break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_write returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: /* socket error */ if(!num) { /* EOF */ if(c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed on SSL_write " "with %d byte(s) in buffer", c->sock_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_DEBUG, "SSL socket closed on SSL_write"); ssl_open_rd=ssl_open_wr=0; /* buggy peer: no close_notify */ } else parse_socket_error(c, "SSL_write"); break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_write"); ssl_open_rd=0; if(!strcmp(SSL_get_version(c->ssl), "SSLv2")) ssl_open_wr=0; break; case SSL_ERROR_SSL: sslerror("SSL_write"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** check write shutdown conditions */ if(sock_open_wr && !ssl_open_rd && !c->ssl_ptr) { s_log(LOG_DEBUG, "Sending socket write shutdown"); sock_open_wr=0; /* no further write allowed */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ } if(ssl_open_wr && !sock_open_rd && !c->sock_ptr) { s_log(LOG_DEBUG, "Sending SSL write shutdown"); ssl_open_wr=0; /* no further write allowed */ if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */ shutdown_wants_write=1; /* initiate close_notify */ } else { /* no alerts in SSLv2 including close_notify alert */ shutdown(c->sock_rfd->fd, SHUT_RD); /* notify the kernel */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ SSL_set_shutdown(c->ssl, /* notify the OpenSSL library */ SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); ssl_open_rd=0; /* no further read allowed */ } } /****************************** check watchdog */ if(++watchdog>100) { /* loop executes without transferring any data */ s_log(LOG_ERR, "transfer() loop executes not transferring any data"); s_log(LOG_ERR, "please report the problem to [email protected]"); stunnel_info(LOG_ERR); s_log(LOG_ERR, "protocol=%s, SSL_pending=%d", SSL_get_version(c->ssl), SSL_pending(c->ssl)); s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s, " "ssl_open_rd=%s, ssl_open_wr=%s", sock_open_rd ? "Y" : "n", sock_open_wr ? "Y" : "n", ssl_open_rd ? "Y" : "n", ssl_open_wr ? "Y" : "n"); s_log(LOG_ERR, "sock_can_rd=%s, sock_can_wr=%s, " "ssl_can_rd=%s, ssl_can_wr=%s", sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n", ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n"); s_log(LOG_ERR, "read_wants_read=%s, read_wants_write=%s", read_wants_read ? "Y" : "n", read_wants_write ? "Y" : "n"); s_log(LOG_ERR, "write_wants_read=%s, write_wants_write=%s", write_wants_read ? "Y" : "n", write_wants_write ? "Y" : "n"); s_log(LOG_ERR, "shutdown_wants_read=%s, shutdown_wants_write=%s", shutdown_wants_read ? "Y" : "n", shutdown_wants_write ? "Y" : "n"); s_log(LOG_ERR, "socket input buffer: %d byte(s), " "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); longjmp(c->err, 1); } } while(sock_open_wr || ssl_open_wr || shutdown_wants_read || shutdown_wants_write); }
NOEXPORT OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req, char *url) { BIO *bio=NULL; OCSP_REQ_CTX *req_ctx=NULL; OCSP_RESPONSE *resp=NULL; int err; char *host=NULL, *port=NULL, *path=NULL; SOCKADDR_UNION addr; int ssl; /* parse the OCSP URL */ if(!OCSP_parse_url(url, &host, &port, &path, &ssl)) { s_log(LOG_ERR, "OCSP: Failed to parse the OCSP URL"); goto cleanup; } if(ssl) { s_log(LOG_ERR, "OCSP: SSL not supported for OCSP" " - additional stunnel service needs to be defined"); goto cleanup; } memset(&addr, 0, sizeof addr); addr.in.sin_family=AF_INET; if(!hostport2addr(&addr, host, port)) { s_log(LOG_ERR, "OCSP: Failed to resolve the OCSP server address"); goto cleanup; } /* connect specified OCSP server (responder) */ c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket"); if(c->fd<0) goto cleanup; if(s_connect(c, &addr, addr_len(&addr))) goto cleanup; bio=BIO_new_fd(c->fd, BIO_NOCLOSE); if(!bio) goto cleanup; s_log(LOG_DEBUG, "OCSP: response retrieved"); /* OCSP protocol communication loop */ req_ctx=OCSP_sendreq_new(bio, path, req, -1); if(!req_ctx) { sslerror("OCSP: OCSP_sendreq_new"); goto cleanup; } while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) { s_poll_init(c->fds); s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio)); err=s_poll_wait(c->fds, c->opt->timeout_busy, 0); if(err==-1) sockerror("OCSP: s_poll_wait"); if(err==0) s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded"); if(err<=0) goto cleanup; } #if 0 s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); #endif /* http://www.mail-archive.com/[email protected]/msg61691.html */ if(resp) { s_log(LOG_DEBUG, "OCSP: request completed"); } else { if(ERR_peek_error()) sslerror("OCSP: OCSP_sendreq_nbio"); else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */ s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error"); } cleanup: if(req_ctx) OCSP_REQ_CTX_free(req_ctx); if(bio) BIO_free_all(bio); if(c->fd>=0) { closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ } if(host) OPENSSL_free(host); if(port) OPENSSL_free(port); if(path) OPENSSL_free(path); return resp; }
/****************************** transfer data */ NOEXPORT void transfer(CLI *c) { int watchdog=0; /* a counter to detect an infinite loop */ int num, err; /* logical channels (not file descriptors!) open for read or write */ int sock_open_rd=1, sock_open_wr=1; /* awaited conditions on SSL file descriptors */ int shutdown_wants_read=0, shutdown_wants_write=0; int read_wants_read=0, read_wants_write=0; int write_wants_read=0, write_wants_write=0; /* actual conditions on file descriptors */ int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr; c->sock_ptr=c->ssl_ptr=0; do { /* main loop of client data transfer */ /****************************** initialize *_wants_* */ read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN) && c->ssl_ptr<BUFFSIZE && !read_wants_write; write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; /****************************** setup c->fds structure */ s_poll_init(c->fds); /* initialize the structure */ /* for plain socket open data strem = open file descriptor */ /* make sure to add each open socket to receive exceptions! */ if(sock_open_rd) /* only poll if the read file descriptor is open */ s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0); if(sock_open_wr) /* only poll if the write file descriptor is open */ s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr); /* poll SSL file descriptors unless SSL shutdown was completed */ if(SSL_get_shutdown(c->ssl)!= (SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN)) { s_poll_add(c->fds, c->ssl_rfd->fd, read_wants_read || write_wants_read || shutdown_wants_read, 0); s_poll_add(c->fds, c->ssl_wfd->fd, 0, read_wants_write || write_wants_write || shutdown_wants_write); } /****************************** wait for an event */ err=s_poll_wait(c->fds, (sock_open_rd && /* both peers open */ !(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)) || c->ssl_ptr /* data buffered to write to socket */ || c->sock_ptr /* data buffered to write to SSL */ ? c->opt->timeout_idle : c->opt->timeout_close, 0); switch(err) { case -1: sockerror("transfer: s_poll_wait"); longjmp(c->err, 1); case 0: /* timeout */ if((sock_open_rd && !(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)) || c->ssl_ptr || c->sock_ptr) { s_log(LOG_INFO, "transfer: s_poll_wait:" " TIMEOUTidle exceeded: sending reset"); longjmp(c->err, 1); } else { /* already closing connection */ s_log(LOG_ERR, "transfer: s_poll_wait:" " TIMEOUTclose exceeded: closing"); return; /* OK */ } } /****************************** check for errors on sockets */ err=s_poll_error(c->fds, c->sock_rfd->fd); if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) { s_log(LOG_NOTICE, "Read socket error: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if(c->sock_wfd->fd!=c->sock_rfd->fd) { /* performance optimization */ err=s_poll_error(c->fds, c->sock_wfd->fd); if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) { s_log(LOG_NOTICE, "Write socket error: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } err=s_poll_error(c->fds, c->ssl_rfd->fd); if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) { s_log(LOG_NOTICE, "SSL socket error: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if(c->ssl_wfd->fd!=c->ssl_rfd->fd) { /* performance optimization */ err=s_poll_error(c->fds, c->ssl_wfd->fd); if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) { s_log(LOG_NOTICE, "SSL socket error: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } /****************************** check for hangup conditions */ if(s_poll_hup(c->fds, c->sock_rfd->fd)) { s_log(LOG_INFO, "Read socket closed (hangup)"); sock_open_rd=0; } if(s_poll_hup(c->fds, c->sock_wfd->fd)) { if(c->ssl_ptr) { s_log(LOG_ERR, "Write socket closed (hangup) with %d unsent byte(s)", c->ssl_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_INFO, "Write socket closed (hangup)"); sock_open_wr=0; } if(s_poll_hup(c->fds, c->ssl_rfd->fd) || s_poll_hup(c->fds, c->ssl_wfd->fd)) { /* hangup -> buggy (e.g. Microsoft) peer: * SSL socket closed without close_notify alert */ if(c->sock_ptr || write_wants_write) { s_log(LOG_ERR, "SSL socket closed (hangup) with %d unsent byte(s)", c->sock_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_INFO, "SSL socket closed (hangup)"); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); } /****************************** retrieve results from c->fds */ sock_can_rd=s_poll_canread(c->fds, c->sock_rfd->fd); sock_can_wr=s_poll_canwrite(c->fds, c->sock_wfd->fd); ssl_can_rd=s_poll_canread(c->fds, c->ssl_rfd->fd); ssl_can_wr=s_poll_canwrite(c->fds, c->ssl_wfd->fd); /****************************** checks for internal failures */ /* please report any internal errors to stunnel-users mailing list */ 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); longjmp(c->err, 1); } if(c->reneg_state==RENEG_DETECTED && !c->opt->option.renegotiation) { s_log(LOG_ERR, "Aborting due to renegotiation request"); longjmp(c->err, 1); } /****************************** send SSL close_notify alert */ if(shutdown_wants_read || shutdown_wants_write) { num=SSL_shutdown(c->ssl); /* send close_notify alert */ if(num<0) /* -1 - not completed */ err=SSL_get_error(c->ssl, num); else /* 0 or 1 - success */ err=SSL_ERROR_NONE; switch(err) { case SSL_ERROR_NONE: /* the shutdown was successfully completed */ s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify alert"); shutdown_wants_read=shutdown_wants_write=0; break; case SSL_ERROR_SYSCALL: /* socket error */ if(parse_socket_error(c, "SSL_shutdown")) break; /* a non-critical error: retry */ SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); shutdown_wants_read=shutdown_wants_write=0; break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying"); shutdown_wants_read=0; shutdown_wants_write=1; break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying"); shutdown_wants_read=1; shutdown_wants_write=0; break; case SSL_ERROR_SSL: /* SSL error */ sslerror("SSL_shutdown"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** read from socket */ if(sock_open_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); switch(num) { case -1: if(parse_socket_error(c, "readsocket")) break; /* a non-critical error: retry */ sock_open_rd=sock_open_wr=0; break; case 0: /* close */ s_log(LOG_INFO, "Read socket closed (readsocket)"); sock_open_rd=0; break; default: c->sock_ptr+=num; watchdog=0; /* reset watchdog */ } } /****************************** write to socket */ if(sock_open_wr && sock_can_wr) { num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); switch(num) { case -1: /* error */ if(parse_socket_error(c, "writesocket")) break; /* a non-critical error: retry */ sock_open_rd=sock_open_wr=0; break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); c->ssl_ptr-=num; c->sock_bytes+=num; watchdog=0; /* reset watchdog */ } } /****************************** update *_wants_* based on new *_ptr */ /* this update is also required for SSL_pending() to be used */ read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN) && c->ssl_ptr<BUFFSIZE && !read_wants_write; write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; /****************************** read from SSL */ if((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) || /* it may be possible to read some pending data after * writesocket() above made some room in c->ssl_buff */ (read_wants_write && ssl_can_wr)) { read_wants_read=0; read_wants_write=0; num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: if(num==0) s_log(LOG_DEBUG, "SSL_read returned 0"); c->ssl_ptr+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); read_wants_write=1; break; case SSL_ERROR_WANT_READ: /* is it possible? */ s_log(LOG_DEBUG, "SSL_read returned WANT_READ: retrying"); read_wants_read=1; break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_read returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if(num && parse_socket_error(c, "SSL_read")) break; /* a non-critical error: retry */ /* EOF -> buggy (e.g. Microsoft) peer: * SSL socket closed without close_notify alert */ if(c->sock_ptr || write_wants_write) { s_log(LOG_ERR, "SSL socket closed (SSL_read) with %d unsent byte(s)", c->sock_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_INFO, "SSL socket closed (SSL_read)"); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_ZERO_RETURN: /* close_notify alert received */ s_log(LOG_INFO, "SSL closed (SSL_read)"); if(SSL_version(c->ssl)==SSL2_VERSION) SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_SSL: sslerror("SSL_read"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** write to SSL */ if((write_wants_read && ssl_can_rd) || (write_wants_write && ssl_can_wr)) { write_wants_read=0; write_wants_write=0; num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: if(num==0) s_log(LOG_DEBUG, "SSL_write returned 0"); memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); c->sock_ptr-=num; c->ssl_bytes+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: /* buffered data? */ s_log(LOG_DEBUG, "SSL_write returned WANT_WRITE: retrying"); write_wants_write=1; break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); write_wants_read=1; break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_write returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: /* socket error */ if(num && parse_socket_error(c, "SSL_write")) break; /* a non-critical error: retry */ /* EOF -> buggy (e.g. Microsoft) peer: * SSL socket closed without close_notify alert */ if(c->sock_ptr) { /* TODO: what about buffered data? */ s_log(LOG_ERR, "SSL socket closed (SSL_write) with %d unsent byte(s)", c->sock_ptr); longjmp(c->err, 1); /* reset the socket */ } s_log(LOG_INFO, "SSL socket closed (SSL_write)"); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_ZERO_RETURN: /* close_notify alert received */ s_log(LOG_INFO, "SSL closed (SSL_write)"); if(SSL_version(c->ssl)==SSL2_VERSION) SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_SSL: sslerror("SSL_write"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); longjmp(c->err, 1); } } /****************************** check write shutdown conditions */ if(sock_open_wr && SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN && !c->ssl_ptr) { sock_open_wr=0; /* no further write allowed */ if(!c->sock_wfd->is_socket) { s_log(LOG_DEBUG, "Closing the file descriptor"); sock_open_rd=0; /* file descriptor is ready to be closed */ } else if(!shutdown(c->sock_wfd->fd, SHUT_WR)) { /* send TCP FIN */ s_log(LOG_DEBUG, "Sent socket write shutdown"); } else { s_log(LOG_DEBUG, "Failed to send socket write shutdown"); sock_open_rd=0; /* file descriptor is ready to be closed */ } } if(!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && !sock_open_rd && !c->sock_ptr && !write_wants_write) { if(SSL_version(c->ssl)!=SSL2_VERSION) { s_log(LOG_DEBUG, "Sending close_notify alert"); shutdown_wants_write=1; } else { /* no alerts in SSLv2, including the close_notify alert */ s_log(LOG_DEBUG, "Closing SSLv2 socket"); if(c->ssl_rfd->is_socket) shutdown(c->ssl_rfd->fd, SHUT_RD); /* notify the kernel */ if(c->ssl_wfd->is_socket) shutdown(c->ssl_wfd->fd, SHUT_WR); /* send TCP FIN */ /* notify the OpenSSL library */ SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); } } /****************************** check watchdog */ if(++watchdog>100) { /* loop executes without transferring any data */ s_log(LOG_ERR, "transfer() loop executes not transferring any data"); s_log(LOG_ERR, "please report the problem to [email protected]"); stunnel_info(LOG_ERR); s_log(LOG_ERR, "protocol=%s, SSL_pending=%d", SSL_get_version(c->ssl), SSL_pending(c->ssl)); s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s", sock_open_rd ? "Y" : "n", sock_open_wr ? "Y" : "n"); s_log(LOG_ERR, "SSL_RECEIVED_SHUTDOWN=%s, SSL_SENT_SHUTDOWN=%s", SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN ? "Y" : "n", SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN ? "Y" : "n"); s_log(LOG_ERR, "sock_can_rd=%s, sock_can_wr=%s", sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n"); s_log(LOG_ERR, "ssl_can_rd=%s, ssl_can_wr=%s", ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n"); s_log(LOG_ERR, "read_wants_read=%s, read_wants_write=%s", read_wants_read ? "Y" : "n", read_wants_write ? "Y" : "n"); s_log(LOG_ERR, "write_wants_read=%s, write_wants_write=%s", write_wants_read ? "Y" : "n", write_wants_write ? "Y" : "n"); s_log(LOG_ERR, "shutdown_wants_read=%s, shutdown_wants_write=%s", shutdown_wants_read ? "Y" : "n", shutdown_wants_write ? "Y" : "n"); s_log(LOG_ERR, "socket input buffer: %d byte(s), " "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); longjmp(c->err, 1); } } while(sock_open_wr || !(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) || shutdown_wants_read || shutdown_wants_write); }
static int init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(ctx))) { sslerror("SSL_new"); return -1; } #if SSLEAY_VERSION_NUMBER >= 0x0922 SSL_set_session_id_context(c->ssl, sid_ctx, strlen(sid_ctx)); #endif if(options.option.client) { if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* Does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* Setup some values for transfer() function */ if(options.option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { if(options.option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_zero(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy)) { case -1: sockerror("init_ssl: s_poll_wait"); return -1; /* error */ case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait timeout"); return -1; /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait unknown result"); return -1; /* error */ } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(options.option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); return -1; } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", options.option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(options.option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } return 0; /* OK */ }
/****************************** transfer data */ static int transfer(CLI *c) { int num, err; 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 */ c->sock_ptr=c->ssl_ptr=0; sock_rd=sock_wr=ssl_rd=ssl_wr=1; c->sock_bytes=c->ssl_bytes=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 */ check_SSL_pending=0; /****************************** setup c->fds structure */ s_poll_zero(&c->fds); /* Initialize the structure */ if(sock_rd && c->sock_ptr<BUFFSIZE) /* socket input buffer not full*/ s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0); if((ssl_rd && c->ssl_ptr<BUFFSIZE) || /* SSL input buffer not full */ ((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_poll_add(&c->fds, c->sock_wfd->fd, 0, 1); 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)) /* want to SSL_read or SSL_shutdown but write to the * underlying socket needed for the SSL protocol */ s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1); /****************************** wait for an event */ err=s_poll_wait(&c->fds, (sock_rd && ssl_rd) /* both peers open */ || c->ssl_ptr /* data buffered to write to socket */ || c->sock_ptr /* data buffered to write to SSL */ ? c->opt->timeout_idle : c->opt->timeout_close); switch(err) { case -1: sockerror("transfer: s_poll_wait"); return -1; case 0: /* timeout */ if((sock_rd && ssl_rd) || c->ssl_ptr || c->sock_ptr) { s_log(LOG_INFO, "s_poll_wait timeout: connection reset"); return -1; } else { /* already closing connection */ s_log(LOG_INFO, "s_poll_wait timeout: connection close"); return 0; /* OK */ } } 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; } /****************************** send SSL close_notify message */ if(ssl_closing==CL_INIT || (ssl_closing==CL_RETRY && ((want_rd && ssl_can_rd) || (want_wr && ssl_can_wr)))) { switch(SSL_shutdown(c->ssl)) { /* Send close_notify */ case 1: /* the shutdown was successfully completed */ s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify"); ssl_closing=CL_CLOSED; /* done! */ break; case 0: /* the shutdown is not yet finished */ s_log(LOG_DEBUG, "SSL_shutdown retrying"); ssl_closing=CL_RETRY; /* retry next time */ break; case -1: /* a fatal error occurred */ sslerror("SSL_shutdown"); return -1; } } /****************************** write to socket */ if(sock_wr && sock_can_wr) { /* for stunnel to tell web server the remote ip address */ add_remote_ip_to_header(c); num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); switch(num) { case -1: /* error */ if(parse_socket_error("writesocket")) return -1; break; case 0: s_log(LOG_DEBUG, "No data written to the socket: retrying"); break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ check_SSL_pending=1; /* check for data buffered by SSL */ c->ssl_ptr-=num; c->sock_bytes+=num; watchdog=0; /* reset watchdog */ } } /****************************** write to SSL */ if(ssl_wr && c->sock_ptr && ( /* output buffer not empty */ ssl_can_wr || (want_rd && ssl_can_rd) /* SSL_write wants to read from the underlying descriptor */ )) { num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); c->sock_ptr-=num; c->ssl_bytes+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_write returned WANT_WRITE: retrying"); break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_write returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: /* really an error */ if(num && parse_socket_error("SSL_write")) return -1; break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_write"); ssl_rd=0; break; case SSL_ERROR_SSL: sslerror("SSL_write"); return -1; default: s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); return -1; } } /****************************** read from socket */ if(sock_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); switch(num) { case -1: if(parse_socket_error("readsocket")) return -1; break; case 0: /* close */ s_log(LOG_DEBUG, "Socket closed on read"); sock_rd=0; break; default: c->sock_ptr+=num; watchdog=0; /* reset watchdog */ } } /****************************** read from SSL */ if(ssl_rd && c->ssl_ptr<BUFFSIZE && ( /* input buffer not full */ ssl_can_rd || (want_wr && ssl_can_wr) || /* SSL_read wants to write to the underlying descriptor */ (check_SSL_pending && SSL_pending(c->ssl)) /* write made space from full buffer */ )) { num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: c->ssl_ptr+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_read returned WANT_READ: retrying"); break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_read returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if(!num) { /* EOF */ if(c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed with %d byte(s) in buffer", c->sock_ptr); return -1; /* reset the socket */ } s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ ssl_closing=CL_CLOSED; /* don't try to send it back */ } else if(parse_socket_error("SSL_read")) return -1; break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_read"); ssl_rd=0; break; case SSL_ERROR_SSL: sslerror("SSL_read"); return -1; default: s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); return -1; } } /****************************** check write shutdown conditions */ if(sock_wr && !ssl_rd && !c->ssl_ptr) { s_log(LOG_DEBUG, "Socket write shutdown"); sock_wr=0; /* no further write allowed */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ } if(ssl_wr && (!sock_rd || SSL_get_shutdown(c->ssl)) && !c->sock_ptr) { s_log(LOG_DEBUG, "SSL write shutdown"); ssl_wr=0; /* no further write allowed */ if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */ ssl_closing=CL_INIT; /* initiate close_notify */ } else { /* no alerts in SSLv2 including close_notify alert */ shutdown(c->sock_rfd->fd, SHUT_RD); /* notify the kernel */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ SSL_set_shutdown(c->ssl, /* notify the OpenSSL library */ SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); ssl_rd=0; /* no further read allowed */ ssl_closing=CL_CLOSED; /* closed */ } } if(ssl_closing==CL_RETRY) { /* SSL shutdown */ if(!want_rd && !want_wr) { /* close_notify alert was received */ s_log(LOG_DEBUG, "SSL doesn't need to read or write"); ssl_closing=CL_CLOSED; } if(watchdog>5) { s_log(LOG_NOTICE, "Too many retries on SSL shutdown"); ssl_closing=CL_CLOSED; } } /****************************** check watchdog */ if(++watchdog>100) { /* loop executes without transferring any data */ s_log(LOG_ERR, "transfer() loop executes not transferring any data"); s_log(LOG_ERR, "please report the problem to [email protected]"); s_log(LOG_ERR, "socket open: rd=%s wr=%s, ssl open: rd=%s wr=%s", sock_rd ? "yes" : "no", sock_wr ? "yes" : "no", ssl_rd ? "yes" : "no", ssl_wr ? "yes" : "no"); s_log(LOG_ERR, "socket ready: rd=%s wr=%s, ssl ready: rd=%s wr=%s", sock_can_rd ? "yes" : "no", sock_can_wr ? "yes" : "no", ssl_can_rd ? "yes" : "no", ssl_can_wr ? "yes" : "no"); s_log(LOG_ERR, "ssl want: rd=%s wr=%s", want_rd ? "yes" : "no", want_wr ? "yes" : "no"); s_log(LOG_ERR, "socket input buffer: %d byte(s), " "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); s_log(LOG_ERR, "check_SSL_pending=%d, ssl_closing=%d", check_SSL_pending, ssl_closing); return -1; } } while(sock_wr || ssl_closing!=CL_CLOSED); return 0; /* OK */ }
void child_status(void) { /* dead libwrap or 'exec' process detected */ int pid, status; char *sig_name; #ifdef HAVE_WAIT_FOR_PID while((pid=wait_for_pid(-1, &status, WNOHANG))>0) { #else if((pid=wait(&status))>0) { #endif #ifdef WIFSIGNALED if(WIFSIGNALED(status)) { sig_name=signal_name(WTERMSIG(status)); s_log(LOG_INFO, "Child process %d terminated on %s", pid, sig_name); str_free(sig_name); } else { s_log(LOG_INFO, "Child process %d finished with code %d", pid, WEXITSTATUS(status)); } #else s_log(LOG_INFO, "Child process %d finished with status %d", pid, status); #endif } } #endif /* !defined(USE_OS2) */ #endif /* !defined(USE_WIN32) */ /**************************************** main loop accepting connections */ void daemon_loop(void) { while(1) { int temporary_lack_of_resources=0; int num=s_poll_wait(fds, -1, -1); if(num>=0) { SERVICE_OPTIONS *opt; s_log(LOG_DEBUG, "Found %d ready file descriptor(s)", num); if(service_options.log_level>=LOG_DEBUG) /* performance optimization */ s_poll_dump(fds, LOG_DEBUG); if(s_poll_canread(fds, signal_pipe[0])) if(signal_pipe_dispatch()) /* SIGNAL_TERMINATE or error */ break; /* terminate daemon_loop */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept && s_poll_canread(fds, opt->fd)) if(accept_connection(opt)) temporary_lack_of_resources=1; } else { log_error(LOG_NOTICE, get_last_socket_error(), "daemon_loop: s_poll_wait"); temporary_lack_of_resources=1; } if(temporary_lack_of_resources) { s_log(LOG_NOTICE, "Accepting new connections suspended for 1 second"); sleep(1); /* to avoid log trashing */ } } } /* return 1 when a short delay is needed before another try */ NOEXPORT int accept_connection(SERVICE_OPTIONS *opt) { SOCKADDR_UNION addr; char *from_address; SOCKET s; socklen_t addrlen; addrlen=sizeof addr; for(;;) { s=s_accept(opt->fd, &addr.sa, &addrlen, 1, "local socket"); if(s!=INVALID_SOCKET) /* success! */ break; switch(get_last_socket_error()) { case S_EINTR: /* interrupted by a signal */ break; /* retry now */ case S_EMFILE: #ifdef S_ENFILE case S_ENFILE: #endif #ifdef S_ENOBUFS case S_ENOBUFS: #endif #ifdef S_ENOMEM case S_ENOMEM: #endif return 1; /* temporary lack of resources */ default: return 0; /* any other error */ } } from_address=s_ntop(&addr, addrlen); s_log(LOG_DEBUG, "Service [%s] accepted (FD=%d) from %s", opt->servname, s, from_address); str_free(from_address); #ifdef USE_FORK RAND_add("", 1, 0.0); /* each child needs a unique entropy pool */ #else if(max_clients && num_clients>=max_clients) { s_log(LOG_WARNING, "Connection rejected: too many clients (>=%ld)", max_clients); closesocket(s); return 0; } #endif if(create_client(opt->fd, s, alloc_client_session(opt, s, s), client_thread)) { s_log(LOG_ERR, "Connection rejected: create_client failed"); closesocket(s); return 0; } return 0; }
static void daemon_loop(void) { SOCKADDR_UNION addr; s_poll_set fds; LOCAL_OPTIONS *opt; get_limits(); s_poll_zero(&fds); #ifndef USE_WIN32 s_poll_add(&fds, signal_pipe_init(), 1, 0); #endif if(!local_options.next) { s_log(LOG_ERR, "No connections defined in config file"); exit(1); } num_clients=0; /* 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 s_poll_add(&fds, opt->fd, 1, 0); } #if !defined (USE_WIN32) && !defined (__vms) if(!(options.option.foreground)) daemonize(); drop_privileges(); create_pid(); #endif /* !defined USE_WIN32 && !defined (__vms) */ /* create exec+connect services */ for(opt=local_options.next; opt; opt=opt->next) { if(opt->option.accept) /* skip ordinary (accepting) services */ continue; enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ num_clients++; leave_critical_section(CRIT_CLIENTS); create_client(-1, -1, alloc_client_session(opt, -1, -1), client); } while(1) { if(s_poll_wait(&fds, -1)<0) /* non-critical error */ log_error(LOG_INFO, get_last_socket_error(), "daemon_loop: s_poll_wait"); else for(opt=local_options.next; opt; opt=opt->next) if(s_poll_canread(&fds, opt->fd)) accept_connection(opt); } s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)"); }
static void transfer(CLI * c) { int watchdog = 0; int num, err; int sock_open_rd = 1, sock_open_wr = 1; int shutdown_wants_read = 0, shutdown_wants_write = 0; int read_wants_read, read_wants_write = 0; int write_wants_read = 0, write_wants_write; int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr; c->sock_ptr = c->ssl_ptr = 0; do { read_wants_read = !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN) && c->ssl_ptr < BUFFSIZE && !read_wants_write; write_wants_write = !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; s_poll_init(c->fds); if (sock_open_rd) s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr < BUFFSIZE, 0); if (sock_open_wr) s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr); if (read_wants_read || write_wants_read || shutdown_wants_read) s_poll_add(c->fds, c->ssl_rfd->fd, 1, 0); if (read_wants_write || write_wants_write || shutdown_wants_write) s_poll_add(c->fds, c->ssl_wfd->fd, 0, 1); err = s_poll_wait(c->fds, (sock_open_rd && !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN)) || c->ssl_ptr || c->sock_ptr ? c->opt->timeout_idle : c->opt->timeout_close, 0); switch (err) { case -1: sockerror("transfer: s_poll_wait"); longjmp(c->err, 1); case 0: if ((sock_open_rd && !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN)) || c->ssl_ptr || c->sock_ptr) { s_log(LOG_INFO, "transfer: s_poll_wait:" " TIMEOUTidle exceeded: sending reset"); longjmp(c->err, 1); } else { s_log(LOG_ERR, "transfer: s_poll_wait:" " TIMEOUTclose exceeded: closing"); return; } } err = s_poll_error(c->fds, c->sock_rfd); if (err) { s_log(LOG_NOTICE, "Error detected on socket (read) file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if (c->sock_wfd->fd != c->sock_rfd->fd) { err = s_poll_error(c->fds, c->sock_wfd); if (err) { s_log(LOG_NOTICE, "Error detected on socket write file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } err = s_poll_error(c->fds, c->ssl_rfd); if (err) { s_log(LOG_NOTICE, "Error detected on SSL (read) file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } if (c->ssl_wfd->fd != c->ssl_rfd->fd) { err = s_poll_error(c->fds, c->ssl_wfd); if (err) { s_log(LOG_NOTICE, "Error detected on SSL write file descriptor: %s (%d)", s_strerror(err), err); longjmp(c->err, 1); } } sock_can_rd = s_poll_canread(c->fds, c->sock_rfd->fd); sock_can_wr = s_poll_canwrite(c->fds, c->sock_wfd->fd); ssl_can_rd = s_poll_canread(c->fds, c->ssl_rfd->fd); ssl_can_wr = s_poll_canwrite(c->fds, c->ssl_wfd->fd); 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); longjmp(c->err, 1); } if (shutdown_wants_read || shutdown_wants_write) { num = SSL_shutdown(c->ssl); if (num < 0) err = SSL_get_error(c->ssl, num); else err = SSL_ERROR_NONE; switch (err) { case SSL_ERROR_NONE: s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify alert"); shutdown_wants_read = shutdown_wants_write = 0; break; case SSL_ERROR_SYSCALL: if (parse_socket_error(c, "SSL_shutdown")) break; SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); shutdown_wants_read = shutdown_wants_write = 0; break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying"); shutdown_wants_read = 0; shutdown_wants_write = 1; break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying"); shutdown_wants_read = 1; shutdown_wants_write = 0; break; case SSL_ERROR_SSL: sslerror("SSL_shutdown"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err); longjmp(c->err, 1); } } if (sock_open_rd && sock_can_rd) { num = readsocket(c->sock_rfd->fd, c->sock_buff + c->sock_ptr, BUFFSIZE - c->sock_ptr); switch (num) { case -1: if (parse_socket_error(c, "readsocket")) break; case 0: s_log(LOG_DEBUG, "Socket closed on read"); sock_open_rd = 0; break; default: c->sock_ptr += num; watchdog = 0; } } if (sock_open_wr && sock_can_wr) { num = writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); switch (num) { case -1: if (parse_socket_error(c, "writesocket")) break; case 0: s_log(LOG_DEBUG, "Socket closed on write"); sock_open_rd = sock_open_wr = 0; break; default: memmove(c->ssl_buff, c->ssl_buff + num, c->ssl_ptr - num); c->ssl_ptr -= num; c->sock_bytes += num; watchdog = 0; } } read_wants_read = !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN) && c->ssl_ptr < BUFFSIZE && !read_wants_write; write_wants_write = !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN) && c->sock_ptr && !write_wants_read; if ((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) || (read_wants_write && ssl_can_wr)) { read_wants_write = 0; num = SSL_read(c->ssl, c->ssl_buff + c->ssl_ptr, BUFFSIZE - c->ssl_ptr); switch (err = SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: if (num == 0) s_log(LOG_DEBUG, "SSL_read returned 0"); c->ssl_ptr += num; watchdog = 0; break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); read_wants_write = 1; break; case SSL_ERROR_WANT_READ: break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_read returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if (num && parse_socket_error(c, "SSL_read")) break; if (c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed on SSL_read with %d unsent byte(s)", c->sock_ptr); longjmp(c->err, 1); } s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_ZERO_RETURN: s_log(LOG_DEBUG, "SSL closed on SSL_read"); if (SSL_version(c->ssl) == SSL2_VERSION) SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_SSL: sslerror("SSL_read"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); longjmp(c->err, 1); } } if ((write_wants_read && ssl_can_rd) || (write_wants_write && ssl_can_wr)) { write_wants_read = 0; num = SSL_write(c->ssl, c->sock_buff, c->sock_ptr); switch (err = SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: if (num == 0) s_log(LOG_DEBUG, "SSL_write returned 0"); memmove(c->sock_buff, c->sock_buff + num, c->sock_ptr - num); c->sock_ptr -= num; c->ssl_bytes += num; watchdog = 0; break; case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); write_wants_read = 1; break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_write returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if (num && parse_socket_error(c, "SSL_write")) break; if (c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed on SSL_write with %d unsent byte(s)", c->sock_ptr); longjmp(c->err, 1); } s_log(LOG_DEBUG, "SSL socket closed on SSL_write"); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_ZERO_RETURN: s_log(LOG_DEBUG, "SSL closed on SSL_write"); if (SSL_version(c->ssl) == SSL2_VERSION) SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); break; case SSL_ERROR_SSL: sslerror("SSL_write"); longjmp(c->err, 1); default: s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); longjmp(c->err, 1); } } if (sock_open_wr && SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN && !c->ssl_ptr) { sock_open_wr = 0; if (!c->sock_wfd->is_socket) { s_log(LOG_DEBUG, "Closing the socket file descriptor"); sock_open_rd = 0; } else if (!shutdown(c->sock_wfd->fd, SHUT_WR)) { s_log(LOG_DEBUG, "Sent socket write shutdown"); } else { s_log(LOG_DEBUG, "Failed to send socket write shutdown"); sock_open_rd = 0; } } if (!(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN) && !sock_open_rd && !c->sock_ptr) { if (SSL_version(c->ssl) != SSL2_VERSION) { s_log(LOG_DEBUG, "Sending close_notify alert"); shutdown_wants_write = 1; } else { s_log(LOG_DEBUG, "Closing SSLv2 socket"); if (c->ssl_rfd->is_socket) shutdown(c->ssl_rfd->fd, SHUT_RD); if (c->ssl_wfd->is_socket) shutdown(c->ssl_wfd->fd, SHUT_WR); SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); } } if (++watchdog > 100) { s_log(LOG_ERR, "transfer() loop executes not transferring any data"); wifisec_info(LOG_ERR); s_log(LOG_ERR, "protocol=%s, SSL_pending=%d", SSL_get_version(c->ssl), SSL_pending(c->ssl)); s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s", sock_open_rd ? "Y" : "n", sock_open_wr ? "Y" : "n"); s_log(LOG_ERR, "SSL_RECEIVED_SHUTDOWN=%s, SSL_SENT_SHUTDOWN=%s", SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN ? "Y" : "n", SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN ? "Y" : "n"); s_log(LOG_ERR, "sock_can_rd=%s, sock_can_wr=%s", sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n"); s_log(LOG_ERR, "ssl_can_rd=%s, ssl_can_wr=%s", ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n"); s_log(LOG_ERR, "read_wants_read=%s, read_wants_write=%s", read_wants_read ? "Y" : "n", read_wants_write ? "Y" : "n"); s_log(LOG_ERR, "write_wants_read=%s, write_wants_write=%s", write_wants_read ? "Y" : "n", write_wants_write ? "Y" : "n"); s_log(LOG_ERR, "shutdown_wants_read=%s, shutdown_wants_write=%s", shutdown_wants_read ? "Y" : "n", shutdown_wants_write ? "Y" : "n"); s_log(LOG_ERR, "socket input buffer: %d byte(s), " "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); longjmp(c->err, 1); } } while (sock_open_wr || !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN) || shutdown_wants_read || shutdown_wants_write); }
NOEXPORT void imap_server(CLI *c) { char *line, *id, *tail, *capa; s_poll_init(c->fds); s_poll_add(c->fds, c->local_rfd.fd, 1, 0); switch(s_poll_wait(c->fds, 0, 200)) { case 0: /* fd not ready to read */ s_log(LOG_DEBUG, "RFC 2595 detected"); break; case 1: /* fd ready to read */ s_log(LOG_DEBUG, "RFC 2595 not detected"); return; /* return if RFC 2595 is not used */ default: /* -1 */ sockerror("RFC2595 (s_poll_wait)"); longjmp(c->err, 1); } /* process server welcome and send it to client */ line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "* OK")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } capa=strstr(line, "CAPABILITY"); if(!capa) capa=strstr(line, "capability"); if(capa) *capa='K'; /* disable CAPABILITY within greeting */ fd_printf(c, c->local_wfd.fd, "%s (stunnel)", line); id=str_dup(""); while(1) { /* process client commands */ str_free(line); line=fd_getline(c, c->local_rfd.fd); /* split line into id and tail */ str_free(id); id=str_dup(line); tail=strchr(id, ' '); if(!tail) break; *tail++='\0'; if(is_prefix(tail, "STARTTLS")) { fd_printf(c, c->local_wfd.fd, "%s OK Begin TLS negotiation now", id); str_free(line); str_free(id); return; /* success */ } else if(is_prefix(tail, "CAPABILITY")) { fd_putline(c, c->remote_fd.fd, line); /* send it to server */ str_free(line); line=fd_getline(c, c->remote_fd.fd); /* get the capabilites */ if(*line=='*') { /* * append STARTTLS * should also add LOGINDISABLED, but can't because * of Mozilla bug #324138/#312009 * LOGIN would fail as "unexpected command", anyway */ fd_printf(c, c->local_wfd.fd, "%s STARTTLS", line); str_free(line); line=fd_getline(c, c->remote_fd.fd); /* next line */ } fd_putline(c, c->local_wfd.fd, line); /* forward to the client */ tail=strchr(line, ' '); if(!tail || !is_prefix(tail+1, "OK")) { /* not OK? */ fd_putline(c, c->local_wfd.fd, "* BYE unexpected server response"); s_log(LOG_ERR, "Unexpected server response: %s", line); break; } } else if(is_prefix(tail, "LOGOUT")) { fd_putline(c, c->local_wfd.fd, "* BYE server terminating"); fd_printf(c, c->local_wfd.fd, "%s OK LOGOUT completed", id); break; } else { fd_putline(c, c->local_wfd.fd, "* BYE stunnel: unexpected command"); fd_printf(c, c->local_wfd.fd, "%s BAD %s unexpected", id, tail); s_log(LOG_ERR, "Unexpected client command %s", tail); break; } } /* clean server shutdown */ str_free(id); fd_putline(c, c->remote_fd.fd, "stunnel LOGOUT"); str_free(line); line=fd_getline(c, c->remote_fd.fd); if(*line=='*') { str_free(line); line=fd_getline(c, c->remote_fd.fd); } str_free(line); longjmp(c->err, 2); /* don't reset */ }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { strcpy(c->accepted_address, "NOT A SOCKET"); c->local_rfd.is_socket=0; c->local_wfd.is_socket=0; /* TODO: It's not always true */ #ifdef USE_WIN32 if(get_last_socket_error()!=ENOTSOCK) { #else if(c->opt->option.transparent_src || get_last_socket_error()!=ENOTSOCK) { #endif sockerror("getpeerbyname"); longjmp(c->err, 1); } /* ignore ENOTSOCK error so 'local' doesn't have to be a socket */ } else { /* success */ /* copy addr to c->peer_addr */ memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); c->peer_addr.num=1; s_ntop(c->accepted_address, &c->peer_addr.addr[0]); c->local_rfd.is_socket=1; c->local_wfd.is_socket=1; /* TODO: It's not always true */ /* it's a socket: lets setup options */ if(set_socket_options(c->local_rfd.fd, 1)<0) longjmp(c->err, 1); #ifdef USE_LIBWRAP libwrap_auth(c); #endif /* USE_LIBWRAP */ auth_user(c); s_log(LOG_NOTICE, "Service %s accepted connection from %s", c->opt->servname, c->accepted_address); } } static void init_remote(CLI *c) { /* create connection to host/service */ if(c->opt->source_addr.num) memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); #ifndef USE_WIN32 else if(c->opt->option.transparent_src) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); #endif else { c->bind_addr.num=0; /* don't bind connecting socket */ } /* setup c->remote_fd, now */ if(c->opt->option.remote) c->remote_fd.fd=connect_remote(c); #ifdef SO_ORIGINAL_DST else if(c->opt->option.transparent_dst) c->remote_fd.fd=connect_transparent(c); #endif /* SO_ORIGINAL_DST */ else /* NOT in remote mode */ c->remote_fd.fd=connect_local(c); c->remote_fd.is_socket=1; /* always! */ s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); if(set_socket_options(c->remote_fd.fd, 2)<0) longjmp(c->err, 1); } static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(c->opt->ctx))) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, strlen(sid_ctx)); if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->host_name) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->host_name); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->host_name)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { #if OPENSSL_VERSION_NUMBER<0x1000002f /* this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ enter_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); #if OPENSSL_VERSION_NUMBER<0x1000002f leave_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; int unsafe_openssl; c->ssl=SSL_new(c->opt->ctx); if(!c->ssl) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->sni) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->sni); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->sni)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sense to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } unsafe_openssl=SSLeay()<0x0090810fL || (SSLeay()>=0x10000000L && SSLeay()<0x1000002fL); while(1) { /* critical section for OpenSSL version < 0.9.8p or 1.x.x < 1.0.0b * * this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * alternative solution is to disable internal session caching * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ if(unsafe_openssl) enter_critical_section(CRIT_SSL); if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); if(unsafe_openssl) leave_critical_section(CRIT_SSL); err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(c->fds); s_poll_add(c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case S_EINTR: case S_EWOULDBLOCK: #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: #endif continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ #ifdef USE_WIN32 win_new_chain(c); #endif if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
static void init_ssl(CLI * c) { int i, err; SSL_SESSION *old_session; c->ssl = SSL_new(c->opt->ctx); if (!c->ssl) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); if (c->opt->option.client) { if (c->opt->session) { SSL_set_session(c->ssl, c->opt->session); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if (c->local_rfd.fd == c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } if (c->opt->option.client) { c->sock_rfd = &(c->local_rfd); c->sock_wfd = &(c->local_wfd); c->ssl_rfd = c->ssl_wfd = &(c->remote_fd); } else { c->sock_rfd = c->sock_wfd = &(c->remote_fd); c->ssl_rfd = &(c->local_rfd); c->ssl_wfd = &(c->local_wfd); } while (1) { if (c->opt->option.client) i = SSL_connect(c->ssl); else i = SSL_accept(c->ssl); err = SSL_get_error(c->ssl, i); if (err == SSL_ERROR_NONE) break; if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { s_poll_init(c->fds); s_poll_add(c->fds, c->ssl_rfd->fd, err == SSL_ERROR_WANT_READ, err == SSL_ERROR_WANT_WRITE); switch (s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; } if (err == SSL_ERROR_SYSCALL) { switch (get_last_socket_error()) { case S_EINTR: case S_EWOULDBLOCK: #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: #endif continue; } } if (c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if (SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { if (c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); old_session = c->opt->session; c->opt->session = SSL_get1_session(c->ssl); if (old_session) SSL_SESSION_free(old_session); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }