/* 发送数据 * * 返回: < 0 错误,0 成功,1 需要重读,2 需要重写 */ int32 ssl_io::send() { assert( "io send fd invalid",_fd > 0 ); if ( !_handshake ) return do_handshake(); size_t bytes = _send->data_size(); assert( "io send without data",bytes > 0 ); int32 len = SSL_write( X_SSL( _ssl_ctx ),_send->data_pointer(),bytes ); if ( expect_true(len > 0) ) { _send->subtract( len ); return ((size_t)len) == bytes ? 0 : 2; } int32 ecode = SSL_get_error( X_SSL( _ssl_ctx ),len ); if ( SSL_ERROR_WANT_WRITE == ecode ) return 2; // 非主动断开,打印错误日志 if ( (SSL_ERROR_ZERO_RETURN == ecode) || (SSL_ERROR_SYSCALL == ecode && 0 == errno) ) { return -1; } SSL_ERROR( "ssl io send" ); return -1; }
// 返回: < 0 错误,0 成功,1 需要重读,2 需要重写 int32 ssl_io::do_handshake() { int32 ecode = SSL_do_handshake( X_SSL( _ssl_ctx ) ); if ( 1 == ecode ) { _handshake = true; // 可能上层在握手期间发送了一些数据,握手成功要检查一下 return _send->data_size() > 0 ? 2 : 0; } /* Caveat: Any TLS/SSL I/O function can lead to either of * SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE. In particular, SSL_read() * or SSL_peek() may want to write data and SSL_write() may want to read * data. This is mainly because TLS/SSL handshakes may occur at any time * during the protocol (initiated by either the client or the server); * SSL_read(), SSL_peek(), and SSL_write() will handle any pending * handshakes. */ ecode = SSL_get_error( X_SSL( _ssl_ctx ),ecode ); if ( SSL_ERROR_WANT_READ == ecode ) return 1; if ( SSL_ERROR_WANT_WRITE == ecode ) return 2; // error SSL_ERROR( "ssl io do handshake:" ); return -1; }
/* 接收数据 * * 返回: < 0 错误,0 成功,1 需要重读,2 需要重写 */ int32 ssl_io::recv() { assert( "io recv fd invalid",_fd > 0 ); if ( !_handshake ) return do_handshake(); if ( !_recv->reserved() ) return -1; /* no more memory */ // ERR_clear_error uint32 size = _recv->buff_size(); int32 len = SSL_read( X_SSL( _ssl_ctx ),_recv->buff_pointer(),size ); if ( expect_true(len > 0) ) { _recv->increase( len ); return 0; } int32 ecode = SSL_get_error( X_SSL( _ssl_ctx ),len ); if ( SSL_ERROR_WANT_READ == ecode ) return 1; /* https://www.openssl.org/docs/manmaster/man3/SSL_read.html * SSL连接关闭时,要先关闭SSL协议,再关闭socket。当一个连接直接关闭时,SSL并不能明确 * 区分开来。SSL_ERROR_ZERO_RETURN仅仅是表示SSL协议层关闭,连接并没有关闭(你可以把一 * 个SSL连接转化为一个非SSL连接,参考SSL_shutdown)。正常关闭下,SSL_read先收到一个 * SSL_ERROR_ZERO_RETURN转换为普通连接,然后read再收到一个0。如果直接关闭,则SSL返回 * 0,SSL_get_error检测到syscall错误(即read返回0),这时errno为0,SSL_get_error并返 * 回0。 */ // 非主动断开,打印错误日志 // 在实际测试中,chrome会直接断开链接,而firefox则会关闭SSL */ if ( (SSL_ERROR_ZERO_RETURN == ecode) || (SSL_ERROR_SYSCALL == ecode && 0 == errno) ) { return -1; } SSL_ERROR( "ssl io recv" ); return -1; }
static int TlsOutputProc(ClientData instanceData, /* Socket state. */ CONST char *buf, /* The data buffer. */ int toWrite, /* How many bytes to write? */ int *errorCodePtr) /* Where to store error code. */ { State *statePtr = (State *) instanceData; int written, err; *errorCodePtr = 0; dprintf(stderr,"\nBIO_write(0x%x, %d)", (unsigned int) statePtr, toWrite); if (statePtr->flags & TLS_TCL_CALLBACK) { /* don't process any bytes while verify callback is running */ written = -1; *errorCodePtr = EAGAIN; goto output; } if (!SSL_is_init_finished(statePtr->ssl)) { written = Tls_WaitForConnect(statePtr, errorCodePtr); if (written <= 0) { goto output; } } if (statePtr->flags & TLS_TCL_INIT) { statePtr->flags &= ~(TLS_TCL_INIT); } if (toWrite == 0) { dprintf(stderr, "zero-write\n"); BIO_flush(statePtr->bio); written = 0; goto output; } else { /* * We need to clear the SSL error stack now because we sometimes reach * this function with leftover errors in the stack. If BIO_write * returns -1 and intends EAGAIN, there is a leftover error, it will be * misconstrued as an error, not EAGAIN. * * Alternatively, we may want to handle the <0 return codes from * BIO_write specially (as advised in the RSA docs). TLS's lower level * BIO functions play with the retry flags though, and this seems to * work correctly. Similar fix in TlsInputProc. - hobbs */ ERR_clear_error(); written = BIO_write(statePtr->bio, buf, toWrite); dprintf(stderr,"\nBIO_write(0x%x, %d) -> [%d]", (unsigned int) statePtr, toWrite, written); } if (written <= 0) { switch ((err = SSL_get_error(statePtr->ssl, written))) { case SSL_ERROR_NONE: if (written < 0) { written = 0; } break; case SSL_ERROR_WANT_WRITE: dprintf(stderr," write W BLOCK"); break; case SSL_ERROR_WANT_READ: dprintf(stderr," write R BLOCK"); break; case SSL_ERROR_WANT_X509_LOOKUP: dprintf(stderr," write X BLOCK"); break; case SSL_ERROR_ZERO_RETURN: dprintf(stderr," closed\n"); written = 0; break; case SSL_ERROR_SYSCALL: *errorCodePtr = Tcl_GetErrno(); dprintf(stderr," [%d] syscall errr: %d", written, *errorCodePtr); written = -1; break; case SSL_ERROR_SSL: Tls_Error(statePtr, SSL_ERROR(statePtr->ssl, written)); *errorCodePtr = ECONNABORTED; written = -1; break; default: dprintf(stderr," unknown err: %d\n", err); break; } } output: dprintf(stderr, "\nOutput(%d) -> %d", toWrite, written); return written; }
static int TlsInputProc(ClientData instanceData, /* Socket state. */ char *buf, /* Where to store data read. */ int bufSize, /* How much space is available * in the buffer? */ int *errorCodePtr) /* Where to store error code. */ { State *statePtr = (State *) instanceData; int bytesRead; /* How many bytes were read? */ *errorCodePtr = 0; dprintf(stderr,"\nBIO_read(%d)", bufSize); if (statePtr->flags & TLS_TCL_CALLBACK) { /* don't process any bytes while verify callback is running */ bytesRead = 0; goto input; } if (!SSL_is_init_finished(statePtr->ssl)) { bytesRead = Tls_WaitForConnect(statePtr, errorCodePtr); if (bytesRead <= 0) { goto input; } } if (statePtr->flags & TLS_TCL_INIT) { statePtr->flags &= ~(TLS_TCL_INIT); } /* * We need to clear the SSL error stack now because we sometimes reach * this function with leftover errors in the stack. If BIO_read * returns -1 and intends EAGAIN, there is a leftover error, it will be * misconstrued as an error, not EAGAIN. * * Alternatively, we may want to handle the <0 return codes from * BIO_read specially (as advised in the RSA docs). TLS's lower level BIO * functions play with the retry flags though, and this seems to work * correctly. Similar fix in TlsOutputProc. - hobbs */ ERR_clear_error(); bytesRead = BIO_read(statePtr->bio, buf, bufSize); dprintf(stderr,"\nBIO_read -> %d", bytesRead); if (bytesRead < 0) { int err = SSL_get_error(statePtr->ssl, bytesRead); if (err == SSL_ERROR_SSL) { Tls_Error(statePtr, SSL_ERROR(statePtr->ssl, bytesRead)); *errorCodePtr = ECONNABORTED; } else if (BIO_should_retry(statePtr->bio)) { dprintf(stderr,"RE! "); *errorCodePtr = EAGAIN; } else { *errorCodePtr = Tcl_GetErrno(); if (*errorCodePtr == ECONNRESET) { /* Soft EOF */ *errorCodePtr = 0; bytesRead = 0; } } } input: dprintf(stderr, "\nInput(%d) -> %d [%d]", bufSize, bytesRead, *errorCodePtr); return bytesRead; }