summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/SChannelTransceiverI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceSSL/SChannelTransceiverI.cpp')
-rw-r--r--cpp/src/IceSSL/SChannelTransceiverI.cpp1463
1 files changed, 1463 insertions, 0 deletions
diff --git a/cpp/src/IceSSL/SChannelTransceiverI.cpp b/cpp/src/IceSSL/SChannelTransceiverI.cpp
new file mode 100644
index 00000000000..ae36570991a
--- /dev/null
+++ b/cpp/src/IceSSL/SChannelTransceiverI.cpp
@@ -0,0 +1,1463 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <IceSSL/SChannelTransceiverI.h>
+
+#include <IceUtil/StringUtil.h>
+
+#include <IceSSL/ConnectionInfo.h>
+#include <IceSSL/Instance.h>
+#include <IceSSL/SSLEngine.h>
+#include <IceSSL/Util.h>
+#include <Ice/Communicator.h>
+#include <Ice/LoggerUtil.h>
+#include <Ice/Buffer.h>
+#include <Ice/LocalException.h>
+
+#ifdef ICE_USE_SCHANNEL
+
+using namespace std;
+using namespace Ice;
+using namespace IceSSL;
+
+namespace
+{
+
+string
+protocolName(DWORD protocol)
+{
+ switch(protocol)
+ {
+ case SP_PROT_SSL2_CLIENT:
+ case SP_PROT_SSL2_SERVER:
+ return "SSL 2.0";
+ case SP_PROT_SSL3_CLIENT:
+ case SP_PROT_SSL3_SERVER:
+ return "SSL 3.0";
+ case SP_PROT_TLS1_CLIENT:
+ case SP_PROT_TLS1_SERVER:
+ return "TLS 1.0";
+ case SP_PROT_TLS1_1_CLIENT:
+ case SP_PROT_TLS1_1_SERVER:
+ return "TLS 1.1";
+ case SP_PROT_TLS1_2_CLIENT:
+ case SP_PROT_TLS1_2_SERVER:
+ return "TLS 1.2";
+ default:
+ return "Unknown";
+ }
+}
+
+string
+trustStatusToString(DWORD status)
+{
+ if(status & CERT_TRUST_NO_ERROR)
+ {
+ return "CERT_TRUST_NO_ERROR";
+ }
+
+ if(status & CERT_TRUST_IS_NOT_TIME_VALID)
+ {
+ return "CERT_TRUST_IS_NOT_TIME_VALID";
+ }
+
+ if(status & CERT_TRUST_IS_REVOKED)
+ {
+ return "CERT_TRUST_IS_REVOKED";
+ }
+
+ if(status & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
+ {
+ return "CERT_TRUST_IS_NOT_SIGNATURE_VALID";
+ }
+
+ if(status & CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
+ {
+ return "CERT_TRUST_IS_NOT_VALID_FOR_USAGE";
+ }
+
+ if(status & CERT_TRUST_IS_UNTRUSTED_ROOT)
+ {
+ return "CERT_TRUST_IS_UNTRUSTED_ROOT";
+ }
+
+ if(status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
+ {
+ return "CERT_TRUST_REVOCATION_STATUS_UNKNOWN";
+ }
+
+ if(status & CERT_TRUST_IS_CYCLIC)
+ {
+ return "CERT_TRUST_IS_CYCLIC";
+ }
+
+ if(status & CERT_TRUST_INVALID_EXTENSION)
+ {
+ return "CERT_TRUST_INVALID_EXTENSION";
+ }
+
+ if(status & CERT_TRUST_INVALID_POLICY_CONSTRAINTS)
+ {
+ return "CERT_TRUST_INVALID_POLICY_CONSTRAINTS";
+ }
+
+ if(status & CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
+ {
+ return "CERT_TRUST_INVALID_BASIC_CONSTRAINTS";
+ }
+
+ if(status & CERT_TRUST_INVALID_NAME_CONSTRAINTS)
+ {
+ return "CERT_TRUST_INVALID_NAME_CONSTRAINTS";
+ }
+
+ if(status & CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT)
+ {
+ return "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT";
+ }
+
+ if(status & CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT)
+ {
+ return "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT";
+ }
+
+ if(status & CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT)
+ {
+ return "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT";
+ }
+
+ if(status & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT)
+ {
+ return "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT";
+ }
+
+ if(status & CERT_TRUST_IS_OFFLINE_REVOCATION)
+ {
+ return "CERT_TRUST_IS_OFFLINE_REVOCATION";
+ }
+
+ if(status & CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY)
+ {
+ return "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY";
+ }
+
+ if(status & CERT_TRUST_IS_EXPLICIT_DISTRUST)
+ {
+ return "CERT_TRUST_IS_EXPLICIT_DISTRUST";
+ }
+
+ if(status & CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT)
+ {
+ return "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT";
+ }
+
+ //
+ // New in Windows 8
+ //
+ //if(status & CERT_TRUST_HAS_WEAK_SIGNATURE)
+ //{
+ // return "CERT_TRUST_HAS_WEAK_SIGNATURE";
+ //}
+
+ if(status & CERT_TRUST_IS_PARTIAL_CHAIN)
+ {
+ return "CERT_TRUST_IS_PARTIAL_CHAIN";
+ }
+
+ if(status & CERT_TRUST_CTL_IS_NOT_TIME_VALID)
+ {
+ return "CERT_TRUST_CTL_IS_NOT_TIME_VALID";
+ }
+
+ if(status & CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID)
+ {
+ return "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID";
+ }
+
+ if(status & CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE)
+ {
+ return "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE";
+ }
+
+ ostringstream os;
+ os << "UNKNOWN TRUST FAILURE: " << status;
+ return os.str();
+}
+
+SecBuffer*
+getSecBufferWithType(const SecBufferDesc& desc, ULONG bufferType)
+{
+ for(ULONG i = 0; i < desc.cBuffers; ++i)
+ {
+ if(desc.pBuffers[i].BufferType == bufferType)
+ {
+ return &desc.pBuffers[i];
+ }
+ }
+ return 0;
+}
+
+}
+
+IceInternal::NativeInfoPtr
+IceSSL::TransceiverI::getNativeInfo()
+{
+ return this;
+}
+
+#ifdef ICE_USE_IOCP
+IceInternal::AsyncInfo*
+IceSSL::TransceiverI::getAsyncInfo(IceInternal::SocketOperation status)
+{
+ switch(status)
+ {
+ case IceInternal::SocketOperationRead:
+ return &_read;
+ case IceInternal::SocketOperationWrite:
+ return &_write;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+#endif
+
+IceInternal::SocketOperation
+IceSSL::TransceiverI::sslHandshake()
+{
+ DWORD flags = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY | ASC_REQ_ALLOCATE_MEMORY |
+ ASC_REQ_STREAM;
+ if(_incoming)
+ {
+ flags |= ASC_REQ_EXTENDED_ERROR;
+ if(_engine->getVerifyPeer() > 0)
+ {
+ flags |= ASC_REQ_MUTUAL_AUTH;
+ }
+ }
+ else
+ {
+ flags |= ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_MANUAL_CRED_VALIDATION | ISC_RET_EXTENDED_ERROR;
+ }
+
+ SECURITY_STATUS err = SEC_E_OK;
+ DWORD ctxFlags = 0;
+
+ while(true)
+ {
+ if(_state == StateConnected)
+ {
+ assert(!_incoming);
+ SecBuffer outBuffer = { 0, SECBUFFER_TOKEN, 0 };
+ SecBufferDesc outBufferDesc = { SECBUFFER_VERSION, 1, &outBuffer };
+
+ err = InitializeSecurityContext(&_credentials, 0, const_cast<char *>(_host.c_str()), flags, 0, 0, 0, 0,
+ &_ssl, &outBufferDesc, &ctxFlags, 0);
+ _sslInitialized = true;
+ if(err != SEC_E_OK && err != SEC_I_CONTINUE_NEEDED)
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: handshake failure:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ //
+ // Copy the data to the write buffer
+ //
+ _writeBuffer.b.resize(outBuffer.cbBuffer);
+ _writeBuffer.i = _writeBuffer.b.begin();
+ memcpy(_writeBuffer.i, outBuffer.pvBuffer, outBuffer.cbBuffer);
+ FreeContextBuffer(outBuffer.pvBuffer);
+
+ _state = StateHandshakeWriteContinue;
+ }
+
+ if(_state == StateHandshakeReadContinue)
+ {
+ // If read buffer is empty, try to read some data.
+ if(_readBuffer.i == _readBuffer.b.begin() && !readRaw(_readBuffer))
+ {
+ return IceInternal::SocketOperationRead;
+ }
+
+ SecBuffer inBuffers[2] = {
+ { static_cast<DWORD>(_readBuffer.i - _readBuffer.b.begin()), SECBUFFER_TOKEN, _readBuffer.b.begin() },
+ { 0, SECBUFFER_EMPTY, 0 }
+ };
+ SecBufferDesc inBufferDesc = { SECBUFFER_VERSION, 2, inBuffers };
+
+ SecBuffer outBuffers[2] = {
+ { 0, SECBUFFER_TOKEN, 0 },
+ { 0, SECBUFFER_ALERT, 0 }
+ };
+ SecBufferDesc outBufferDesc = { SECBUFFER_VERSION, 2, outBuffers };
+
+ if(_incoming)
+ {
+ err = AcceptSecurityContext(&_credentials, (_sslInitialized ? &_ssl : 0), &inBufferDesc, flags, 0,
+ &_ssl, &outBufferDesc, &ctxFlags, 0);
+ _sslInitialized = true;
+ }
+ else
+ {
+ err = InitializeSecurityContext(&_credentials, &_ssl, const_cast<char*>(_host.c_str()), flags, 0, 0,
+ &inBufferDesc, 0, 0, &outBufferDesc, &ctxFlags, 0);
+ }
+
+ //
+ // If the message is incomplete we need to read more data.
+ //
+ if(err == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ SecBuffer* missing = getSecBufferWithType(inBufferDesc, SECBUFFER_MISSING);
+ size_t pos = _readBuffer.i - _readBuffer.b.begin();
+ _readBuffer.b.resize(missing ? (pos + missing->cbBuffer) : (pos * 2));
+ _readBuffer.i = _readBuffer.b.begin() + pos;
+ return IceInternal::SocketOperationRead;
+ }
+ else if(err != SEC_I_CONTINUE_NEEDED && err != SEC_E_OK)
+ {
+ throw SecurityException(__FILE__, __LINE__, "SSL handshake failure:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ //
+ // Copy out security tokens to the write buffer if any.
+ //
+ SecBuffer* token = getSecBufferWithType(outBufferDesc, SECBUFFER_TOKEN);
+ assert(token);
+ if(token->cbBuffer)
+ {
+ _writeBuffer.b.resize(static_cast<size_t>(token->cbBuffer));
+ _writeBuffer.i = _writeBuffer.b.begin();
+ memcpy(_writeBuffer.i, token->pvBuffer, token->cbBuffer);
+ FreeContextBuffer(token->pvBuffer);
+ }
+
+ //
+ // Check for remaining data in the input buffer.
+ //
+ SecBuffer* extra = getSecBufferWithType(inBufferDesc, SECBUFFER_EXTRA);
+ if(extra)
+ {
+ // Shift the extra data to the start of the input buffer
+ memmove(_readBuffer.b.begin(), _readBuffer.i - extra->cbBuffer, extra->cbBuffer);
+ _readBuffer.i = _readBuffer.b.begin() + extra->cbBuffer;
+ }
+ else
+ {
+ _readBuffer.i = _readBuffer.b.begin();
+ }
+
+ if(token->cbBuffer)
+ {
+ _state = StateHandshakeWriteContinue; // Continue writing if we have a token.
+ }
+ else if(err == SEC_E_OK)
+ {
+ break; // We're done.
+ }
+
+ // Otherwise continue either reading credentials
+ }
+
+ if(_state == StateHandshakeWriteContinue)
+ {
+ //
+ // Write any pending data.
+ //
+ if(!writeRaw(_writeBuffer))
+ {
+ return IceInternal::SocketOperationWrite;
+ }
+ if(err == SEC_E_OK)
+ {
+ break; // Token is written and we weren't told to continue, so we're done!
+ }
+ assert(err == SEC_I_CONTINUE_NEEDED);
+ _state = StateHandshakeReadContinue;
+ }
+ }
+
+ //
+ // Check if the requested capabilities are met
+ //
+ if(flags != ctxFlags)
+ {
+ if(_incoming)
+ {
+ if(!(ctxFlags & ASC_REQ_SEQUENCE_DETECT))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup sequence detect");
+ }
+
+ if(!(ctxFlags & ASC_REQ_REPLAY_DETECT))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup replay detect");
+ }
+
+ if(!(ctxFlags & ASC_REQ_CONFIDENTIALITY))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup confidentiality");
+ }
+
+ if(!(ctxFlags & ASC_REQ_EXTENDED_ERROR))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup extended error");
+ }
+
+ if(!(ctxFlags & ASC_REQ_ALLOCATE_MEMORY))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup memory allocation");
+ }
+
+ if(!(ctxFlags & ASC_REQ_STREAM))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup stream");
+ }
+ }
+ else
+ {
+ if(!(ctxFlags & ISC_REQ_SEQUENCE_DETECT))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup sequence detect");
+ }
+
+ if(!(ctxFlags & ISC_REQ_REPLAY_DETECT))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup replay detect");
+ }
+
+ if(!(ctxFlags & ISC_REQ_CONFIDENTIALITY))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup confidentiality");
+ }
+
+ if(!(ctxFlags & ISC_REQ_EXTENDED_ERROR))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup extended error");
+ }
+
+ if(!(ctxFlags & ISC_REQ_ALLOCATE_MEMORY))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup memory allocation");
+ }
+
+ if(!(ctxFlags & ISC_REQ_STREAM))
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: SChannel failed to setup stream");
+ }
+ }
+ }
+
+ err = QueryContextAttributes(&_ssl, SECPKG_ATTR_STREAM_SIZES, &_sizes);
+ if(err != SEC_E_OK)
+ {
+ throw SecurityException(__FILE__, __LINE__, "IceSSL: failure to query stream sizes attributes:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ size_t pos = _readBuffer.i - _readBuffer.b.begin();
+ if(pos <= (_sizes.cbHeader + _sizes.cbMaximumMessage + _sizes.cbTrailer))
+ {
+ _readBuffer.b.resize(_sizes.cbHeader + _sizes.cbMaximumMessage + _sizes.cbTrailer);
+ _readBuffer.i = _readBuffer.b.begin() + pos;
+ }
+
+ _writeBuffer.b.reset();
+ _writeBuffer.i = _writeBuffer.b.begin();
+
+ return IceInternal::SocketOperationNone;
+}
+
+//
+// Try to decrypt a message and return the number of bytes decrypted, if the number of bytes
+// decrypted is less than the size requested it means that the application needs to read more
+// data before it can decrypt the complete message.
+//
+size_t
+IceSSL::TransceiverI::decryptMessage(IceInternal::Buffer& buffer)
+{
+ assert(_readBuffer.i != _readBuffer.b.begin() || !_readUnprocessed.b.empty());
+
+ //
+ // First check if there is data in the unprocessed buffer.
+ //
+ size_t length = std::min(static_cast<size_t>(buffer.b.end() - buffer.i), _readUnprocessed.b.size());
+ if(length > 0)
+ {
+ memcpy(buffer.i, _readUnprocessed.b.begin(), length);
+ memmove(_readUnprocessed.b.begin(), _readUnprocessed.b.begin() + length, _readUnprocessed.b.size() - length);
+ _readUnprocessed.b.resize(_readUnprocessed.b.size() - length);
+ }
+
+ while(true)
+ {
+ //
+ // If we have filled the buffer or if nothing left to read from
+ // the read buffer, we're done.
+ //
+ Byte* i = buffer.i + length;
+ if(i == buffer.b.end() || _readBuffer.i == _readBuffer.b.begin())
+ {
+ break;
+ }
+
+ //
+ // Try to decrypt the buffered data.
+ //
+ SecBuffer inBuffers[4] = {
+ { static_cast<DWORD>(_readBuffer.i - _readBuffer.b.begin()), SECBUFFER_DATA, _readBuffer.b.begin() },
+ { 0, SECBUFFER_EMPTY, 0 },
+ { 0, SECBUFFER_EMPTY, 0 },
+ { 0, SECBUFFER_EMPTY, 0 }
+ };
+ SecBufferDesc inBufferDesc = { SECBUFFER_VERSION, 4, inBuffers };
+
+ SECURITY_STATUS err = DecryptMessage(&_ssl, &inBufferDesc, 0, 0);
+ if(err == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ //
+ // There isn't enough data to decrypt the message. The input
+ // buffer is resized to the SSL max message size after the SSL
+ // handshake completes so an incomplete message can only occur
+ // if the read buffer is not full.
+ //
+ assert(_readBuffer.i != _readBuffer.b.end());
+ return length;
+ }
+ else if(err == SEC_I_CONTEXT_EXPIRED || err == SEC_I_RENEGOTIATE)
+ {
+ //
+ // The message sender has finished using the connection and
+ // has initiated a shutdown.
+ //
+ throw ConnectionLostException(__FILE__, __LINE__, 0);
+ }
+ else if(err == SEC_E_MESSAGE_ALTERED || err == SEC_E_DECRYPT_FAILURE)
+ {
+ //
+ // Forcefully closing a connection can result in SSL_read
+ // reporting "decryption failed or bad record mac". We trap
+ // that error and treat it as the loss of a connection.
+ //
+ throw ConnectionLostException(__FILE__, __LINE__, 0);
+ }
+ else if(err != SEC_E_OK)
+ {
+ throw ProtocolException(__FILE__, __LINE__, "IceSSL: protocol error during read:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ SecBuffer* dataBuffer = getSecBufferWithType(inBufferDesc, SECBUFFER_DATA);
+ assert(dataBuffer);
+ DWORD remaining = min(static_cast<DWORD>(buffer.b.end() - i), dataBuffer->cbBuffer);
+ length += remaining;
+ if(remaining)
+ {
+ memcpy(i, dataBuffer->pvBuffer, remaining);
+
+ //
+ // Copy remaining decrypted data to unprocessed buffer
+ //
+ if(dataBuffer->cbBuffer > remaining)
+ {
+ _readUnprocessed.b.resize(dataBuffer->cbBuffer - remaining);
+ memcpy(_readUnprocessed.b.begin(), reinterpret_cast<Byte*>(dataBuffer->pvBuffer) + remaining,
+ dataBuffer->cbBuffer - remaining);
+ }
+ }
+
+ //
+ // Move any remaining encrypted data to the begining of the input buffer
+ //
+ SecBuffer* extraBuffer = getSecBufferWithType(inBufferDesc, SECBUFFER_EXTRA);
+ if(extraBuffer && extraBuffer->cbBuffer > 0)
+ {
+ memmove(_readBuffer.b.begin(), _readBuffer.i - extraBuffer->cbBuffer, extraBuffer->cbBuffer);
+ _readBuffer.i = _readBuffer.b.begin() + extraBuffer->cbBuffer;
+ }
+ else
+ {
+ _readBuffer.i = _readBuffer.b.begin();
+ }
+ }
+ return length;
+}
+
+//
+// Encrypt a message and return the number of bytes that has been encrypted, if the
+// number of bytes is less than the message size, the function must be called again.
+//
+size_t
+IceSSL::TransceiverI::encryptMessage(IceInternal::Buffer& buffer)
+{
+ //
+ // Limit the message size to cbMaximumMessage which is the maximun size data that can be
+ // embeded in a SSL record.
+ //
+ DWORD length = std::min(static_cast<DWORD>(buffer.b.end() - buffer.i), _sizes.cbMaximumMessage);
+
+ //
+ // Resize the buffer to hold the encrypted data
+ //
+ _writeBuffer.b.resize(_sizes.cbHeader + length + _sizes.cbTrailer);
+ _writeBuffer.i = _writeBuffer.b.begin();
+
+ SecBuffer buffers[4] = {
+ { _sizes.cbHeader, SECBUFFER_STREAM_HEADER, _writeBuffer.i },
+ { length, SECBUFFER_DATA, _writeBuffer.i + _sizes.cbHeader },
+ { _sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, _writeBuffer.i + _sizes.cbHeader + length },
+ { 0, SECBUFFER_EMPTY, 0 }
+ };
+ SecBufferDesc buffersDesc = { SECBUFFER_VERSION, 4, buffers };
+
+ // Data is encrypted in place, copy the data to be encrypted to the data buffer.
+ memcpy(buffers[1].pvBuffer, buffer.i, length);
+
+ SECURITY_STATUS err = EncryptMessage(&_ssl, 0, &buffersDesc, 0);
+ if(err != SEC_E_OK)
+ {
+ throw ProtocolException(__FILE__, __LINE__, "IceSSL: protocol error encrypting message:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ // EncryptMessage resizes the buffers, so resize the write buffer as well to reflect this.
+ _writeBuffer.b.resize(buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer);
+ _writeBuffer.i = _writeBuffer.b.begin();
+
+ return length;
+}
+
+IceInternal::SocketOperation
+IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer, bool& hasMoreData)
+{
+ try
+ {
+ if(_state == StateNeedConnect)
+ {
+ _state = StateConnectPending;
+ return IceInternal::SocketOperationConnect;
+ }
+ else if(_state <= StateConnectPending)
+ {
+ IceInternal::doFinishConnectAsync(_fd, _write);
+
+ _desc = IceInternal::fdToString(_fd, _proxy, _addr, true);
+
+ if(_proxy)
+ {
+ //
+ // Prepare the read & write buffers in advance.
+ //
+ _proxy->beginWriteConnectRequest(_addr, writeBuffer);
+ _proxy->beginReadConnectRequestResponse(readBuffer);
+
+ //
+ // Return SocketOperationWrite to indicate we need to start a write.
+ //
+ _state = StateProxyConnectRequest; // Send proxy connect request
+ return IceInternal::SocketOperationWrite;
+ }
+
+ _state = StateConnected;
+ }
+ else if(_state == StateProxyConnectRequest)
+ {
+ //
+ // Write completed.
+ //
+ _proxy->endWriteConnectRequest(writeBuffer);
+ _state = StateProxyConnectRequestPending; // Wait for proxy response
+ return IceInternal::SocketOperationRead;
+ }
+ else if(_state == StateProxyConnectRequestPending)
+ {
+ //
+ // Read completed.
+ //
+ _proxy->endReadConnectRequestResponse(readBuffer);
+ _state = StateConnected;
+ }
+
+ assert(_state >= StateConnected && _state <= StateHandshakeWriteContinue);
+
+ if(!_credentialsInitialized)
+ {
+ _readBuffer.b.resize(2048);
+ _readBuffer.i = _readBuffer.b.begin();
+
+ _credentials = _engine->newCredentialsHandle(_incoming);
+ _credentialsInitialized = true;
+ }
+
+ IceInternal::SocketOperation op = sslHandshake();
+ if(op != IceInternal::SocketOperationNone)
+ {
+ return op;
+ }
+
+ if(!_incoming || _engine->getVerifyPeer() > 0)
+ {
+ //
+ // Build the peer certificate chain and verify it.
+ //
+ PCCERT_CONTEXT cert = 0;
+ SECURITY_STATUS err = QueryContextAttributes(&_ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);
+ if(err && err != SEC_E_NO_CREDENTIALS)
+ {
+ throw ProtocolException(__FILE__, __LINE__, "IceSSL: certificate verification failure:" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ if(!cert && (!_incoming || _engine->getVerifyPeer() == 2))
+ {
+ // Clients require server certificate if VerifyPeer>0
+ // and servers require client certificate if
+ // VerifyPeer=2
+ throw ProtocolException(__FILE__, __LINE__, "IceSSL: certificate required:" +
+ IceUtilInternal::lastErrorToString());
+ }
+ else if(cert) // Verify the remote certificate
+ {
+ try
+ {
+ CERT_CHAIN_PARA chainP;
+ memset(&chainP, 0, sizeof(chainP));
+ chainP.cbSize = sizeof(chainP);
+
+ PCCERT_CHAIN_CONTEXT certChain;
+ if(!CertGetCertificateChain(_engine->chainEngine(), cert, 0, 0, &chainP,
+ CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, 0, &certChain))
+ {
+ CertFreeCertificateContext(cert);
+ throw IceUtilInternal::lastErrorToString();
+ }
+
+ CERT_SIMPLE_CHAIN* simpleChain = certChain->rgpChain[0];
+
+ string trustError;
+ if(simpleChain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
+ {
+ trustError = trustStatusToString(certChain->TrustStatus.dwErrorStatus);
+ }
+
+ CertFreeCertificateChain(certChain);
+ CertFreeCertificateContext(cert);
+ if(!trustError.empty())
+ {
+ throw trustError;
+ }
+ }
+ catch(const string& reason)
+ {
+ if(_engine->getVerifyPeer() == 0)
+ {
+ if(_instance->traceLevel() >= 1)
+ {
+ _instance->logger()->trace(_instance->traceCategory(),
+ "IceSSL: ignoring certificate verification failure\n" + reason);
+ }
+ }
+ else
+ {
+ ostringstream os;
+ os << "IceSSL: certificate verification failure\n" << reason;
+ string msg = os.str();
+ if(_instance->traceLevel() >= 1)
+ {
+ _instance->logger()->trace(_instance->traceCategory(), msg);
+ }
+ throw ProtocolException(__FILE__, __LINE__, msg);
+ }
+ }
+ }
+ }
+ _engine->verifyPeer(_fd, _host, getNativeConnectionInfo());
+ _state = StateHandshakeComplete;
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ if(_instance->traceLevel() >= 2)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "failed to establish " << _instance->protocol() << " connection\n";
+ if(_incoming)
+ {
+ out << IceInternal::fdToString(_fd) << "\n" << ex;
+ }
+ else
+ {
+ out << IceInternal::fdToString(_fd, _proxy, _addr, false) << "\n" << ex;
+ }
+ }
+ throw;
+ }
+
+ if(_instance->traceLevel() >= 1)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ if(_incoming)
+ {
+ out << "accepted " << _instance->protocol() << " connection\n" << _desc;
+ }
+ else
+ {
+ out << _instance->protocol() << " connection established\n" << _desc;
+ }
+ }
+
+ if(_instance->engine()->securityTraceLevel() >= 1)
+ {
+ string sslCipherName;
+ string sslKeyExchangeAlgorithm;
+ string sslProtocolName;
+ SecPkgContext_ConnectionInfo info;
+ if(QueryContextAttributes(&_ssl, SECPKG_ATTR_CONNECTION_INFO, &info) == SEC_E_OK)
+ {
+ sslCipherName = _engine->getCipherName(info.aiCipher);
+ sslKeyExchangeAlgorithm = _engine->getCipherName(info.aiExch);
+ sslProtocolName = protocolName(info.dwProtocol);
+ }
+
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "SSL summary for " << (_incoming ? "incoming" : "outgoing") << " connection\n";
+
+ if(sslCipherName.empty())
+ {
+ out << "unknown cipher\n";
+ }
+ else
+ {
+ out << "cipher = " << sslCipherName
+ << "\nkey exchange = " << sslKeyExchangeAlgorithm
+ << "\nprotocol = " << sslProtocolName << "\n";
+ }
+ out << IceInternal::fdToString(_fd);
+ }
+ hasMoreData = !_readUnprocessed.b.empty() || _readBuffer.i != _readBuffer.b.begin();
+ return IceInternal::SocketOperationNone;
+}
+
+IceInternal::SocketOperation
+IceSSL::TransceiverI::closing(bool initiator, const Ice::LocalException&)
+{
+ // If we are initiating the connection closure, wait for the peer
+ // to close the TCP/IP connection. Otherwise, close immediately.
+ return initiator ? IceInternal::SocketOperationRead : IceInternal::SocketOperationNone;
+}
+
+void
+IceSSL::TransceiverI::close()
+{
+ if(_state == StateHandshakeComplete && _instance->traceLevel() >= 1)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "closing " << _instance->protocol() << " connection\n" << toString();
+ }
+ if(_sslInitialized)
+ {
+ DeleteSecurityContext(&_ssl);
+ }
+
+ if(_credentialsInitialized)
+ {
+ FreeCredentialsHandle(&_credentials);
+ }
+
+ assert(_fd != INVALID_SOCKET);
+ try
+ {
+ IceInternal::closeSocket(_fd);
+ _fd = INVALID_SOCKET;
+ }
+ catch(const SocketException&)
+ {
+ _fd = INVALID_SOCKET;
+ throw;
+ }
+}
+
+IceInternal::SocketOperation
+IceSSL::TransceiverI::write(IceInternal::Buffer& buf)
+{
+ if(buf.i == buf.b.end())
+ {
+ return IceInternal::SocketOperationNone;
+ }
+ assert(_state == StateHandshakeComplete);
+
+ while(buf.i != buf.b.end())
+ {
+ if(_bufferedW == 0)
+ {
+ assert(_writeBuffer.i == _writeBuffer.b.end());
+ _bufferedW = encryptMessage(buf);
+ }
+
+ if(!writeRaw(_writeBuffer))
+ {
+ return IceInternal::SocketOperationWrite;
+ }
+
+ assert(_writeBuffer.i == _writeBuffer.b.end()); // Finished writing the encrypted data
+
+ if(_instance->traceLevel() >= 3)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "sent " << _bufferedW << " of " << (buf.b.end() - buf.i) << " bytes via " << _instance->protocol()
+ << '\n' << toString();
+ }
+
+ buf.i += _bufferedW;
+ _bufferedW = 0;
+ }
+ return IceInternal::SocketOperationNone;
+}
+
+IceInternal::SocketOperation
+IceSSL::TransceiverI::read(IceInternal::Buffer& buf, bool& hasMoreData)
+{
+ if(buf.i == buf.b.end())
+ {
+ return IceInternal::SocketOperationNone;
+ }
+ assert(_state == StateHandshakeComplete);
+
+ hasMoreData = false;
+ while(buf.i != buf.b.end())
+ {
+ if(_readUnprocessed.b.empty() && _readBuffer.i == _readBuffer.b.begin() && !readRaw(_readBuffer))
+ {
+ return IceInternal::SocketOperationRead;
+ }
+
+ size_t decrypted = decryptMessage(buf);
+ if(decrypted == 0)
+ {
+ if(!readRaw(_readBuffer))
+ {
+ return IceInternal::SocketOperationRead;
+ }
+ continue;
+ }
+
+ if(_instance->traceLevel() >= 3)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "received " << decrypted << " of " << buf.b.end() - buf.i << " bytes via " << _instance->protocol()
+ << '\n' << toString();
+ }
+ buf.i += decrypted;
+ }
+ hasMoreData = !_readUnprocessed.b.empty() || _readBuffer.i != _readBuffer.b.begin();
+ return IceInternal::SocketOperationNone;
+}
+
+#ifdef ICE_USE_IOCP
+
+bool
+IceSSL::TransceiverI::startWrite(IceInternal::Buffer& buffer)
+{
+ if(_state == StateConnectPending)
+ {
+ IceInternal::Address addr = _proxy ? _proxy->getAddress() : _addr;
+ doConnectAsync(_fd, addr, _write);
+ return false;
+ }
+
+ IceInternal::Buffer& buf = _state == StateProxyConnectRequest ? buffer : _writeBuffer;
+
+ if(_state == StateHandshakeComplete && _bufferedW == 0)
+ {
+ assert(_writeBuffer.i == _writeBuffer.b.end());
+ _bufferedW = encryptMessage(buf);
+ }
+
+ assert(buf.i != buf.b.end());
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+ if(_maxSendPacketSize > 0 && packetSize > _maxSendPacketSize)
+ {
+ packetSize = _maxSendPacketSize;
+ }
+ assert(packetSize > 0);
+ _write.buf.len = static_cast<DWORD>(packetSize);
+ _write.buf.buf = reinterpret_cast<char*>(buf.i);
+ int err = WSASend(_fd, &_write.buf, 1, &_write.count, 0, &_write, NULL);
+ if(err == SOCKET_ERROR)
+ {
+ if(!IceInternal::wouldBlock())
+ {
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ }
+ return packetSize == static_cast<int>(buf.b.end() - buf.i);
+}
+
+void
+IceSSL::TransceiverI::finishWrite(IceInternal::Buffer& buf)
+{
+ if(_state < StateConnected && _state != StateProxyConnectRequest)
+ {
+ return;
+ }
+
+ if(static_cast<int>(_write.count) == SOCKET_ERROR)
+ {
+ WSASetLastError(_write.error);
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+
+ if(_state == StateProxyConnectRequest)
+ {
+ buf.i += _write.count;
+ }
+ else
+ {
+ _writeBuffer.i += _write.count;
+ if(_writeBuffer.i == _writeBuffer.b.end())
+ {
+ if(_instance->traceLevel() >= 3)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "sent " << _bufferedW << " of " << (buf.b.end() - buf.i) << " bytes via "
+ << _instance->protocol() << '\n' << toString();
+ }
+ buf.i += _bufferedW;
+ _bufferedW = 0;
+ }
+ }
+}
+
+void
+IceSSL::TransceiverI::startRead(IceInternal::Buffer& buffer)
+{
+ IceInternal::Buffer& buf = _state == StateProxyConnectRequest ? buffer : _readBuffer;
+
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+ if(_maxReceivePacketSize > 0 && packetSize > _maxReceivePacketSize)
+ {
+ packetSize = _maxReceivePacketSize;
+ }
+ assert(!buf.b.empty() && buf.i != buf.b.end());
+
+ _read.buf.len = static_cast<DWORD>(packetSize);
+ _read.buf.buf = reinterpret_cast<char*>(buf.i);
+
+ int err = WSARecv(_fd, &_read.buf, 1, &_read.count, &_read.flags, &_read, NULL);
+ if(err == SOCKET_ERROR)
+ {
+ if(!IceInternal::wouldBlock())
+ {
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ }
+}
+
+void
+IceSSL::TransceiverI::finishRead(IceInternal::Buffer& buf, bool& hasMoreData)
+{
+ if(static_cast<int>(_read.count) == SOCKET_ERROR)
+ {
+ WSASetLastError(_read.error);
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ else if(_read.count == 0)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
+ }
+
+ if(_state == StateProxyConnectRequest)
+ {
+ buf.i += _read.count;
+ }
+ else
+ {
+ _readBuffer.i += _read.count;
+ if(_state == StateHandshakeComplete)
+ {
+ size_t decrypted = decryptMessage(buf);
+ if(decrypted > 0)
+ {
+ if(_instance->traceLevel() >= 3)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << "received " << decrypted << " of " << buf.b.end() - buf.i << " bytes via "
+ << _instance->protocol() << '\n' << toString();
+ }
+ buf.i += decrypted;
+ hasMoreData = !_readUnprocessed.b.empty() || _readBuffer.i != _readBuffer.b.begin();
+ }
+ else
+ {
+ hasMoreData = false;
+ }
+ }
+ }
+}
+#endif
+
+string
+IceSSL::TransceiverI::protocol() const
+{
+ return _instance->protocol();
+}
+
+string
+IceSSL::TransceiverI::toString() const
+{
+ return _desc;
+}
+
+Ice::ConnectionInfoPtr
+IceSSL::TransceiverI::getInfo() const
+{
+ return getNativeConnectionInfo();
+}
+
+void
+IceSSL::TransceiverI::checkSendSize(const IceInternal::Buffer& buf, size_t messageSizeMax)
+{
+ if(buf.b.size() > messageSizeMax)
+ {
+ IceInternal::Ex::throwMemoryLimitException(__FILE__, __LINE__, buf.b.size(), messageSizeMax);
+ }
+}
+
+IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const IceInternal::NetworkProxyPtr& proxy,
+ const string& host, const IceInternal::Address& addr) :
+ IceInternal::NativeInfo(fd),
+ _instance(instance),
+ _engine(SChannelEnginePtr::dynamicCast(instance->engine())),
+ _proxy(proxy),
+ _host(host),
+ _addr(addr),
+ _incoming(false),
+ _state(StateNeedConnect),
+ _writeBuffer(0),
+ _bufferedW(0),
+ _readBuffer(0),
+ _readUnprocessed(0),
+ _sslInitialized(false),
+ _credentialsInitialized(false)
+#ifdef ICE_USE_IOCP
+ , _read(IceInternal::SocketOperationRead),
+ _write(IceInternal::SocketOperationWrite)
+#endif
+{
+ IceInternal::setBlock(fd, false);
+ IceInternal::setTcpBufSize(fd, _instance->properties(), _instance->logger());
+
+ //
+ // On Windows, limiting the buffer size is important to prevent
+ // poor throughput performances when transfering large amount of
+ // data. See Microsoft KB article KB823764.
+ //
+ _maxSendPacketSize = IceInternal::getSendBufferSize(_fd) / 2;
+ if(_maxSendPacketSize < 512)
+ {
+ _maxSendPacketSize = 0;
+ }
+
+ _maxReceivePacketSize = IceInternal::getRecvBufferSize(_fd);
+ if(_maxReceivePacketSize < 512)
+ {
+ _maxReceivePacketSize = 0;
+ }
+
+#ifndef ICE_USE_IOCP
+ IceInternal::Address connectAddr = proxy ? proxy->getAddress() : addr;
+ if(IceInternal::doConnect(_fd, connectAddr))
+ {
+ _state = StateConnected;
+ _desc = IceInternal::fdToString(_fd, _proxy, _addr, true);
+ if(_instance->traceLevel() >= 1)
+ {
+ Trace out(_instance->logger(), _instance->traceCategory());
+ out << _instance->protocol() << " connection established\n" << _desc;
+ }
+ }
+ else
+ {
+ _desc = IceInternal::fdToString(_fd, _proxy, _addr, true);
+ }
+#endif
+}
+
+IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const string& adapterName) :
+ IceInternal::NativeInfo(fd),
+ _instance(instance),
+ _engine(SChannelEnginePtr::dynamicCast(instance->engine())),
+ _addr(IceInternal::Address()),
+ _adapterName(adapterName),
+ _incoming(true),
+ _state(StateHandshakeReadContinue),
+ _desc(IceInternal::fdToString(fd)),
+ _writeBuffer(0),
+ _bufferedW(0),
+ _readBuffer(0),
+ _readUnprocessed(0),
+ _sslInitialized(false),
+ _credentialsInitialized(false)
+#ifdef ICE_USE_IOCP
+ , _read(IceInternal::SocketOperationRead),
+ _write(IceInternal::SocketOperationWrite)
+#endif
+{
+ IceInternal::setBlock(fd, false);
+ IceInternal::setTcpBufSize(fd, _instance->properties(), _instance->logger());
+
+ //
+ // On Windows, limiting the buffer size is important to prevent
+ // poor throughput performances when transfering large amount of
+ // data. See Microsoft KB article KB823764.
+ //
+ _maxSendPacketSize = IceInternal::getSendBufferSize(_fd) / 2;
+ if(_maxSendPacketSize < 512)
+ {
+ _maxSendPacketSize = 0;
+ }
+
+ _maxReceivePacketSize = IceInternal::getRecvBufferSize(_fd);
+ if(_maxReceivePacketSize < 512)
+ {
+ _maxReceivePacketSize = 0;
+ }
+}
+
+IceSSL::TransceiverI::~TransceiverI()
+{
+ assert(_fd == INVALID_SOCKET);
+}
+
+NativeConnectionInfoPtr
+IceSSL::TransceiverI::getNativeConnectionInfo() const
+{
+ NativeConnectionInfoPtr info = new NativeConnectionInfo();
+ IceInternal::fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort);
+
+ if(_sslInitialized)
+ {
+ CtxtHandle* ssl = const_cast<CtxtHandle*>(&_ssl);
+ PCCERT_CONTEXT cert = 0;
+ PCCERT_CHAIN_CONTEXT certChain = 0;
+ SECURITY_STATUS err = QueryContextAttributes(ssl, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);
+ if(err == SEC_E_OK)
+ {
+ CERT_CHAIN_PARA chainP;
+ memset(&chainP, 0, sizeof(chainP));
+ chainP.cbSize = sizeof(chainP);
+
+ if(CertGetCertificateChain(_engine->chainEngine(), cert, 0, 0, &chainP,
+ CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, 0, &certChain))
+ {
+ CERT_SIMPLE_CHAIN* simpleChain = certChain->rgpChain[0];
+ for(DWORD i = 0; i < simpleChain->cElement; ++i)
+ {
+ PCCERT_CONTEXT c = simpleChain->rgpElement[i]->pCertContext;
+ PCERT_SIGNED_CONTENT_INFO cc;
+
+ DWORD length = 0;
+ if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_CERT, c->pbCertEncoded,
+ c->cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, 0, &cc, &length))
+ {
+ CertFreeCertificateChain(certChain);
+ CertFreeCertificateContext(cert);
+ throw SecurityException(__FILE__, __LINE__,
+ "IceSSL: error decoding peer certificate chain:\n" +
+ IceUtilInternal::lastErrorToString());
+ }
+
+ CertificatePtr certificate = new Certificate(cc);
+ info->nativeCerts.push_back(certificate);
+ info->certs.push_back(certificate->encode());
+ }
+ CertFreeCertificateChain(certChain);
+ }
+ }
+ CertFreeCertificateContext(cert);
+
+ SecPkgContext_ConnectionInfo connInfo;
+ if(QueryContextAttributes(ssl, SECPKG_ATTR_CONNECTION_INFO, &connInfo) == SEC_E_OK)
+ {
+ info->cipher = _engine->getCipherName(connInfo.aiCipher);
+ }
+ }
+
+ info->adapterName = _adapterName;
+ info->incoming = _incoming;
+ return info;
+}
+
+bool
+IceSSL::TransceiverI::writeRaw(IceInternal::Buffer& buf)
+{
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+#ifdef ICE_USE_IOCP
+ //
+ // Limit packet size to avoid performance problems on WIN32
+ //
+ if(_maxSendPacketSize > 0 && packetSize > _maxSendPacketSize)
+ {
+ packetSize = _maxSendPacketSize;
+ }
+#endif
+
+ while(buf.i != buf.b.end())
+ {
+ assert(_fd != INVALID_SOCKET);
+
+ int ret = ::send(_fd, reinterpret_cast<const char*>(buf.i), packetSize, 0);
+ if(ret == 0)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
+ }
+
+ if(ret == SOCKET_ERROR)
+ {
+ if(IceInternal::interrupted())
+ {
+ continue;
+ }
+
+ if(IceInternal::noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ continue;
+ }
+
+ if(IceInternal::wouldBlock())
+ {
+ return false;
+ }
+
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ buf.i += ret;
+ if(packetSize > static_cast<int>(buf.b.end() - buf.i))
+ {
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ }
+ return true;
+}
+
+bool
+IceSSL::TransceiverI::readRaw(IceInternal::Buffer& buf)
+{
+ assert(buf.i != buf.b.end());
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+ Byte* i = buf.i;
+ while(buf.i != buf.b.end())
+ {
+ assert(_fd != INVALID_SOCKET);
+ ssize_t ret = ::recv(_fd, reinterpret_cast<char*>(buf.i), packetSize, 0);
+
+ if(ret == 0)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
+ }
+
+ if(ret == SOCKET_ERROR)
+ {
+ if(IceInternal::interrupted())
+ {
+ continue;
+ }
+
+ if(IceInternal::noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ continue;
+ }
+
+ if(IceInternal::wouldBlock())
+ {
+ return buf.i != i;
+ }
+
+ if(IceInternal::connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = IceInternal::getSocketErrno();
+ throw ex;
+ }
+ }
+ buf.i += ret;
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ return buf.i != i;
+}
+#endif