示例#1
0
/****************************** 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 */
}
示例#2
0
/****************************** 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);
}
示例#3
0
文件: client.c 项目: l7s/stunnel
/****************************** 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);
}
示例#4
0
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);
}