Esempio n. 1
0
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
}
Esempio n. 2
0
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 */
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
File: client.c Progetto: 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);
}
Esempio n. 5
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);
}
Esempio n. 6
0
void log_error(int level, int error, const char *txt) { /* generic error */
    s_log(level, "%s: %s (%d)", txt, s_strerror(error), error);
}
Esempio n. 7
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);
}