rt_public void wide_listen(void) { /* Listen on all the file descriptors opened for reading until the * connected socket is broken. */ /* Make sure we listen on the connected socket and call the handling * routine whenever data is available there. */ #ifdef EIF_WINDOWS if (-1 == add_input(app_sp, (STREAM_FN) arqsthandle)) { #else if (-1 == add_input(app_sp, arqsthandle)) { #endif #ifdef USE_ADD_LOG add_log(4, "add_input: %s (%s)", s_strerror(), s_strname()); #endif dexit(1); } #ifdef USE_ADD_LOG add_log(12, "in listen"); #endif /* After having selected, we scan all our files to make sure none of them * has been removed from the selection process. If at least one is missing, * we are exiting immediately. */ #ifdef EIF_WINDOWS while (0 <= do_select(0)) { #ifdef USE_ADD_LOG add_log(12, "in while do_select"); #endif if (!has_input(app_sp)) { /* Socket connection broken? */ #ifdef USE_ADD_LOG add_log(12, "in !has_input which is what we want"); #endif return; /* Anyway, abort processing */ } } #ifdef USE_ADD_LOG add_log(12, "out of listen"); #endif #else while (0 < do_select((struct timeval *) 0)) { if (!has_input(app_sp)) /* Socket connection broken? */ return; /* Anyway, abort processing */ } #endif #ifdef USE_ADD_LOG add_log(12, "do_select: %s (%s)", s_strerror(), s_strname()); #endif }
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 */ }
const char* s_strerror( int errn ) { static char errmsg[512]; size_t len; errmsg[0]='\0'; if ( !FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, errn, 0, errmsg, 511, NULL ) ) { return s_strerror(s_errno); } len = strlen( errmsg ); while ( len > 0 && strchr( "\r\n", errmsg[len-1] ) ) { errmsg[len-1] = '\0'; len--; } return errmsg; }
/****************************** 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); }
/****************************** 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); }
void log_error(int level, int error, const char *txt) { /* generic error */ s_log(level, "%s: %s (%d)", txt, s_strerror(error), error); }
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); }