You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by sh...@apache.org on 2010/01/23 01:16:35 UTC

svn commit: r902318 - in /qpid/trunk/qpid/cpp/src: ./ qpid/broker/windows/ qpid/client/ qpid/client/windows/ qpid/sys/ qpid/sys/posix/ qpid/sys/windows/

Author: shuston
Date: Sat Jan 23 00:16:34 2010
New Revision: 902318

URL: http://svn.apache.org/viewvc?rev=902318&view=rev
Log:
Add SSL support for Windows client and broker per QPID-1403. Adds new AsynchIO::BufferBase::squish() method that does what used to be done by in-place memmove() calls so it can be reused easily.
SSL support for Windows is in:
- Client: qpid/client/windows/SslConnector.cpp
          qpid/client/TCPConnector.{h cpp} rearranged a bit to make pieces available to SslConnector
- Broker: qpid/broker/windows/SslProtocolFactory.cpp
- Common: qpid/sys/windows/SslAsynchIO contains all the Schannel stuff to negotiate a session, encrypt, and decrypt data. The SslAsynchIO acts as a shim between the layer above and the "regular" AsynchIO that actually handles read/write and completions.

Added:
    qpid/trunk/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
    qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
    qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
    qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h
Modified:
    qpid/trunk/qpid/cpp/src/CMakeLists.txt
    qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.cpp
    qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.h
    qpid/trunk/qpid/cpp/src/qpid/sys/AsynchIO.h
    qpid/trunk/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
    qpid/trunk/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
    qpid/trunk/qpid/cpp/src/ssl.cmake

Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original)
+++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Sat Jan 23 00:16:34 2010
@@ -439,7 +439,7 @@
     ${sslbroker_windows_SOURCES}
   )
   set (qpidbroker_platform_LIBS
-    ${windows_ssl_libs}
+    ${windows_ssl_libs} ${windows_ssl_server_libs}
   )
   set (qpidclient_platform_SOURCES
     qpid/client/windows/SaslFactory.cpp

Added: qpid/trunk/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp?rev=902318&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp Sat Jan 23 00:16:34 2010
@@ -0,0 +1,302 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+#include <boost/bind.hpp>
+#include <memory>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+struct SslServerOptions : qpid::Options
+{
+    std::string certStore;
+    std::string certName;
+    uint16_t port;
+    bool clientAuth;
+
+    SslServerOptions() : certStore("My"), port(5671), clientAuth(false)
+    {
+        qpid::TcpAddress me;
+        if (qpid::sys::SystemInfo::getLocalHostname(me))
+            certName = me.host;
+        else
+            certName = "localhost";
+
+        addOptions()
+            ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
+            ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
+            ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
+            ("ssl-require-client-authentication", optValue(clientAuth), 
+             "Forces clients to authenticate in order to establish an SSL connection");
+    }
+};
+
+class SslProtocolFactory : public qpid::sys::ProtocolFactory {
+    const bool tcpNoDelay;
+    const uint16_t listeningPort;
+    std::string brokerHost;
+    const bool clientAuthSelected;
+    qpid::sys::Socket listener;
+    std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
+    ConnectFailedCallback connectFailedCallback;
+    CredHandle credHandle;
+
+  public:
+    SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+    ~SslProtocolFactory();
+    void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
+    void connect(sys::Poller::shared_ptr, const std::string& host, int16_t port,
+                 sys::ConnectionCodec::Factory*,
+                 ConnectFailedCallback failed);
+
+    uint16_t getPort() const;
+    std::string getHost() const;
+    bool supports(const std::string& capability);
+
+  private:
+    void connectFailed(const qpid::sys::Socket&,
+                       int err,
+                       const std::string& msg);
+    void established(sys::Poller::shared_ptr,
+                     const qpid::sys::Socket&,
+                     sys::ConnectionCodec::Factory*,
+                     bool isClient);
+};
+
+// Static instance to initialise plugin
+static struct SslPlugin : public Plugin {
+    SslServerOptions options;
+
+    Options* getOptions() { return &options; }
+
+    void earlyInitialize(Target&) {
+    }
+    
+    void initialize(Target& target) {
+        broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+        // Only provide to a Broker
+        if (broker) {
+            try {
+                const broker::Broker::Options& opts = broker->getOptions();
+                ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
+                                                                            opts.connectionBacklog,
+                                                                            opts.tcpNoDelay));
+                QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+                broker->registerProtocolFactory("ssl", protocol);
+            } catch (const std::exception& e) {
+                QPID_LOG(error, "Failed to initialise SSL listener: " << e.what());
+            }
+        }
+    }
+} sslPlugin;
+
+SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
+                                       int backlog,
+                                       bool nodelay)
+    : tcpNoDelay(nodelay),
+      listeningPort(options.port),
+      clientAuthSelected(options.clientAuth) {
+
+    SecInvalidateHandle(&credHandle);
+
+    // Get the certificate for this server.
+    HCERTSTORE certStoreHandle;
+    certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
+                                      X509_ASN_ENCODING,
+                                      0,
+                                      CERT_SYSTEM_STORE_LOCAL_MACHINE,
+                                      options.certStore.c_str());
+    if (!certStoreHandle)
+        throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+
+    PCCERT_CONTEXT certContext;
+    certContext = ::CertFindCertificateInStore(certStoreHandle,
+                                               X509_ASN_ENCODING,
+                                               0,
+                                               CERT_FIND_SUBJECT_STR_A,
+                                               options.certName.c_str(),
+                                               NULL);
+    if (certContext == NULL) {
+        int err = ::GetLastError();
+        ::CertCloseStore(certStoreHandle, 0);
+        throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+        throw QPID_WINDOWS_ERROR(err);
+    }
+
+    SCHANNEL_CRED cred;
+    memset(&cred, 0, sizeof(cred));
+    cred.dwVersion = SCHANNEL_CRED_VERSION;
+    cred.cCreds = 1;
+    cred.paCred = &certContext;
+    SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+                                                        UNISP_NAME,
+                                                        SECPKG_CRED_INBOUND,
+                                                        NULL,
+                                                        &cred,
+                                                        NULL,
+                                                        NULL,
+                                                        &credHandle,
+                                                        NULL);
+    if (status != SEC_E_OK)
+        throw QPID_WINDOWS_ERROR(status);
+    listener.listen(options.port, backlog);
+    ::CertFreeCertificateContext(certContext);
+    ::CertCloseStore(certStoreHandle, 0);
+}
+
+SslProtocolFactory::~SslProtocolFactory() {
+    ::FreeCredentialsHandle(&credHandle);
+}
+
+void SslProtocolFactory::connectFailed(const qpid::sys::Socket&,
+                                       int err,
+                                       const std::string& msg) {
+    if (connectFailedCallback)
+        connectFailedCallback(err, msg);
+}
+
+void SslProtocolFactory::established(sys::Poller::shared_ptr poller,
+                                     const qpid::sys::Socket& s,
+                                     sys::ConnectionCodec::Factory* f,
+                                     bool isClient) {
+    sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getPeerAddress(), f);
+
+    if (tcpNoDelay) {
+        s.setTcpNoDelay();
+        QPID_LOG(info,
+                 "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+    }
+
+    SslAsynchIO *aio;
+    if (isClient) {
+        async->setClient();
+        aio =
+          new qpid::sys::windows::ClientSslAsynchIO(brokerHost,
+                                                    s,
+                                                    credHandle,
+                                                    boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+                                                    boost::bind(&AsynchIOHandler::eof, async, _1),
+                                                    boost::bind(&AsynchIOHandler::disconnect, async, _1),
+                                                    boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+                                                    boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+                                                    boost::bind(&AsynchIOHandler::idle, async, _1));
+    }
+    else {
+        aio =
+          new qpid::sys::windows::ServerSslAsynchIO(clientAuthSelected,
+                                                    s,
+                                                    credHandle,
+                                                    boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+                                                    boost::bind(&AsynchIOHandler::eof, async, _1),
+                                                    boost::bind(&AsynchIOHandler::disconnect, async, _1),
+                                                    boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+                                                    boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+                                                    boost::bind(&AsynchIOHandler::idle, async, _1));
+    }
+
+    async->init(aio, 4);
+    aio->start(poller);
+}
+
+uint16_t SslProtocolFactory::getPort() const {
+    return listeningPort; // Immutable no need for lock.
+}
+
+std::string SslProtocolFactory::getHost() const {
+    return listener.getSockname();
+}
+
+void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
+                                sys::ConnectionCodec::Factory* fact) {
+    acceptor.reset(
+        AsynchAcceptor::create(listener,
+                               boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
+    acceptor->start(poller);
+}
+
+void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
+                                 const std::string& host,
+                                 int16_t port,
+                                 sys::ConnectionCodec::Factory* fact,
+                                 ConnectFailedCallback failed)
+{
+    SCHANNEL_CRED cred;
+    memset(&cred, 0, sizeof(cred));
+    cred.dwVersion = SCHANNEL_CRED_VERSION;
+    SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+                                                        UNISP_NAME,
+                                                        SECPKG_CRED_OUTBOUND,
+                                                        NULL,
+                                                        &cred,
+                                                        NULL,
+                                                        NULL,
+                                                        &credHandle,
+                                                        NULL);
+    if (status != SEC_E_OK)
+        throw QPID_WINDOWS_ERROR(status);
+
+    brokerHost = host;
+    // Note that the following logic does not cause a memory leak.
+    // The allocated Socket is freed either by the AsynchConnector
+    // upon connection failure or by the AsynchIO upon connection
+    // shutdown.  The allocated AsynchConnector frees itself when it
+    // is no longer needed.
+    qpid::sys::Socket* socket = new qpid::sys::Socket();
+    connectFailedCallback = failed;
+    AsynchConnector::create(*socket,
+                            host,
+                            port,
+                            boost::bind(&SslProtocolFactory::established,
+                                        this, poller, _1, fact, true),
+                            boost::bind(&SslProtocolFactory::connectFailed,
+                                        this, _1, _2, _3));
+}
+
+namespace
+{
+const std::string SSL = "ssl";
+}
+
+bool SslProtocolFactory::supports(const std::string& capability)
+{
+    std::string s = capability;
+    transform(s.begin(), s.end(), s.begin(), tolower);
+    return s == SSL;
+}
+
+}}} // namespace qpid::sys::windows

Modified: qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.cpp?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.cpp Sat Jan 23 00:16:34 2010
@@ -108,15 +108,23 @@
                        0, // closed
                        0, // nobuffs
                        boost::bind(&TCPConnector::writebuff, this, _1));
+    start(aio);
+    initAmqp();
+    aio->start(poller);
+}
+
+void TCPConnector::start(sys::AsynchIO* aio_) {
+    aio = aio_;
     for (int i = 0; i < 32; i++) {
         aio->queueReadBuffer(new Buff(maxFrameSize));
     }
 
     identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+}
+
+void TCPConnector::initAmqp() {
     ProtocolInitiation init(version);
     writeDataBlock(init);
-
-    aio->start(poller);
 }
 
 void TCPConnector::connectFailed(const std::string& msg) {
@@ -286,7 +294,7 @@
 }
 
 void TCPConnector::writeDataBlock(const AMQDataBlock& data) {
-    AsynchIO::BufferBase* buff = new Buff(maxFrameSize);
+    AsynchIO::BufferBase* buff = aio->getQueuedBuffer();
     framing::Buffer out(buff->bytes, buff->byteCount);
     data.encode(out);
     buff->dataCount = data.encodedSize();

Modified: qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.h?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/client/TCPConnector.h Sat Jan 23 00:16:34 2010
@@ -76,19 +76,12 @@
     boost::shared_ptr<sys::Poller> poller;
     std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
 
-    ~TCPConnector();
-
     void handleClosed();
     bool closeInternal();
 
-    void connected(const sys::Socket&);
-    void connectFailed(const std::string& msg);
-    bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
-    void writebuff(qpid::sys::AsynchIO&);
+    virtual void connected(const sys::Socket&);
     void writeDataBlock(const framing::AMQDataBlock& data);
-    void eof(qpid::sys::AsynchIO&);
 
-    void connect(const std::string& host, int port);
     void close();
     void send(framing::AMQFrame& frame);
     void abort();
@@ -105,6 +98,16 @@
     size_t encode(const char* buffer, size_t size);
     bool canEncode();
 
+protected:
+    virtual ~TCPConnector();
+    void connect(const std::string& host, int port);
+    void start(sys::AsynchIO* aio_);
+    void initAmqp();
+    virtual void connectFailed(const std::string& msg);
+    bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+    void writebuff(qpid::sys::AsynchIO&);
+    void eof(qpid::sys::AsynchIO&);
+
 public:
     TCPConnector(boost::shared_ptr<sys::Poller>,
               framing::ProtocolVersion pVersion,

Added: qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp?rev=902318&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp Sat Jan 23 00:16:34 2010
@@ -0,0 +1,181 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/TCPConnector.h"
+
+#include "config.h"
+#include "qpid/Msg.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/windows/check.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include <memory.h>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+#include <winsock2.h>
+
+namespace qpid {
+namespace client {
+namespace windows {
+
+using namespace qpid::sys;
+using boost::format;
+using boost::str;
+
+
+class SslConnector : public qpid::client::TCPConnector
+{
+    qpid::sys::windows::ClientSslAsynchIO *shim;
+	boost::shared_ptr<qpid::sys::Poller> poller;
+    std::string brokerHost;
+    SCHANNEL_CRED cred;
+    CredHandle credHandle;
+    TimeStamp credExpiry;
+
+    virtual ~SslConnector();
+    void negotiationDone(SECURITY_STATUS status);
+
+    // A number of AsynchIO callbacks go right through to TCPConnector, but
+    // we can't boost::bind to a protected ancestor, so these methods redirect
+    // to those TCPConnector methods.
+    bool redirectReadbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+    void redirectWritebuff(qpid::sys::AsynchIO&);
+    void redirectEof(qpid::sys::AsynchIO&);
+
+public:
+    SslConnector(boost::shared_ptr<qpid::sys::Poller>,
+                 framing::ProtocolVersion pVersion,
+                 const ConnectionSettings&, 
+                 ConnectionImpl*);
+    virtual void connect(const std::string& host, int port);
+    virtual void connected(const Socket&);
+    unsigned int getSSF();
+};
+
+// Static constructor which registers connector here
+namespace {
+    Connector* create(boost::shared_ptr<qpid::sys::Poller> p,
+                      framing::ProtocolVersion v,
+                      const ConnectionSettings& s,
+                      ConnectionImpl* c) {
+        return new SslConnector(p, v, s, c);
+    }
+
+    struct StaticInit {
+        StaticInit() {
+            try {
+                Connector::registerFactory("ssl", &create);
+            } catch (const std::exception& e) {
+                QPID_LOG(error, "Failed to initialise SSL connector: " << e.what());
+            }
+        };
+        ~StaticInit() { }
+    } init;
+}
+
+void SslConnector::negotiationDone(SECURITY_STATUS status)
+{
+    if (status == SEC_E_OK)
+        initAmqp();
+    else
+        connectFailed(QPID_MSG(qpid::sys::strError(status)));
+}
+
+bool SslConnector::redirectReadbuff(qpid::sys::AsynchIO& a,
+                                    qpid::sys::AsynchIOBufferBase* b) {
+    return readbuff(a, b);
+}
+
+void SslConnector::redirectWritebuff(qpid::sys::AsynchIO& a) {
+    writebuff(a);
+}
+
+void SslConnector::redirectEof(qpid::sys::AsynchIO& a) {
+    eof(a);
+}
+
+SslConnector::SslConnector(boost::shared_ptr<qpid::sys::Poller> p,
+                           framing::ProtocolVersion ver,
+                           const ConnectionSettings& settings,
+                           ConnectionImpl* cimpl)
+    : TCPConnector(p, ver, settings, cimpl), shim(0), poller(p)
+{
+    memset(&cred, 0, sizeof(cred));
+    cred.dwVersion = SCHANNEL_CRED_VERSION;
+    SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+                                                        UNISP_NAME,
+                                                        SECPKG_CRED_OUTBOUND,
+                                                        NULL,
+                                                        &cred,
+                                                        NULL,
+                                                        NULL,
+                                                        &credHandle,
+                                                        &credExpiry);
+    if (status != SEC_E_OK)
+        throw QPID_WINDOWS_ERROR(status);
+    QPID_LOG(debug, "SslConnector created for " << ver.toString());
+}
+
+SslConnector::~SslConnector()
+{
+    ::FreeCredentialsHandle(&credHandle);
+}
+
+  // Will this get reach via virtual method via boost::bind????
+
+void SslConnector::connect(const std::string& host, int port) {
+    brokerHost = host;
+    TCPConnector::connect(host, port);
+}
+
+void SslConnector::connected(const Socket& s) {
+    shim = new qpid::sys::windows::ClientSslAsynchIO(brokerHost,
+                                                     s,
+                                                     credHandle,
+                                                     boost::bind(&SslConnector::redirectReadbuff, this, _1, _2),
+                                                     boost::bind(&SslConnector::redirectEof, this, _1),
+                                                     boost::bind(&SslConnector::redirectEof, this, _1),
+                                                     0, // closed
+                                                     0, // nobuffs
+                                                     boost::bind(&SslConnector::redirectWritebuff, this, _1),
+                                                     boost::bind(&SslConnector::negotiationDone, this, _1));
+    start(shim);
+	shim->start(poller);
+}
+
+unsigned int SslConnector::getSSF()
+{
+    return shim->getSslKeySize();
+}
+
+}}} // namespace qpid::client::windows

Modified: qpid/trunk/qpid/cpp/src/qpid/sys/AsynchIO.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/AsynchIO.h?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/AsynchIO.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/AsynchIO.h Sat Jan 23 00:16:34 2010
@@ -87,6 +87,13 @@
     
     virtual ~AsynchIOBufferBase()
     {}
+
+    void squish() {
+        if (dataStart != 0) {
+            memmove(bytes, bytes + dataStart, dataCount);
+            dataStart = 0;
+        }
+    }
 };
 
 /*

Modified: qpid/trunk/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp Sat Jan 23 00:16:34 2010
@@ -319,10 +319,7 @@
 
 void AsynchIO::unread(BufferBase* buff) {
     assert(buff);
-    if (buff->dataStart != 0) {
-        memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount);
-        buff->dataStart = 0;
-    }
+    buff->squish();
 
     bool queueWasEmpty = bufferQueue.empty();
     bufferQueue.push_front(buff);

Modified: qpid/trunk/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp Sat Jan 23 00:16:34 2010
@@ -204,7 +204,6 @@
         if (failCallback)
             failCallback(socket, -1, std::string(e.what()));
         socket.close();
-        delete &socket;
     }
 }
 
@@ -428,10 +427,7 @@
 
 void AsynchIO::unread(AsynchIO::BufferBase* buff) {
     assert(buff);
-    if (buff->dataStart != 0) {
-        memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount);
-        buff->dataStart = 0;
-    }
+    buff->squish();
     QLock l(bufferQueueLock);
     bufferQueue.push_front(buff);
 }

Added: qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp?rev=902318&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp Sat Jan 23 00:16:34 2010
@@ -0,0 +1,654 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "SslAsynchIO.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Time.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/sys/windows/check.h"
+
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+#include <queue>
+#include <boost/bind.hpp>
+
+namespace {
+
+    /*
+     * To make the SSL encryption more efficient, set up a new BufferBase
+     * that leaves room for the SSL header to be prepended and the SSL
+     * trailer to be appended.
+     *
+     * This works by accepting a properly formed BufferBase, remembering it,
+     * and resetting the members of this struct to reflect the reserved
+     * header and trailer areas. It's only needed for giving buffers up to
+     * the frame layer for writing into.
+     */
+    struct SslIoBuff : public qpid::sys::AsynchIO::BufferBase {
+        std::auto_ptr<qpid::sys::AsynchIO::BufferBase> aioBuff;
+
+        SslIoBuff (qpid::sys::AsynchIO::BufferBase *base,
+                   const SecPkgContext_StreamSizes &sizes)
+          : qpid::sys::AsynchIO::BufferBase(&base->bytes[sizes.cbHeader],
+                                            std::min(base->byteCount - sizes.cbHeader - sizes.cbTrailer,
+                                                     sizes.cbMaximumMessage)),
+            aioBuff(base)
+        {}
+
+        ~SslIoBuff() {}
+        qpid::sys::AsynchIO::BufferBase* release() { return aioBuff.release(); }
+    };
+}
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s,
+                         CredHandle hCred,
+                         ReadCallback rCb,
+                         EofCallback eofCb,
+                         DisconnectCallback disCb,
+                         ClosedCallback cCb,
+                         BuffersEmptyCallback eCb,
+                         IdleCallback iCb,
+                         NegotiateDoneCallback nCb) :
+    credHandle(hCred),
+    aio(0),
+    state(Negotiating),
+    readCallback(rCb),
+    idleCallback(iCb),
+    negotiateDoneCallback(nCb),
+    callbacksInProgress(0),
+    queuedDelete(false),
+    leftoverPlaintext(0)
+{
+    SecInvalidateHandle(&ctxtHandle);
+    peerAddress = s.getPeerAddress();
+    aio = qpid::sys::AsynchIO::create(s,
+                                      boost::bind(&SslAsynchIO::sslDataIn, this, _1, _2),
+                                      eofCb,
+                                      disCb,
+                                      cCb,
+                                      eCb,
+                                      boost::bind(&SslAsynchIO::idle, this, _1));
+}
+
+SslAsynchIO::~SslAsynchIO() {
+    if (leftoverPlaintext) {
+        delete leftoverPlaintext;
+        leftoverPlaintext = 0;
+    }
+}
+
+void SslAsynchIO::queueForDeletion() {
+    // This method effectively disconnects the layer above; pass it on the
+    // AsynchIO and delete.
+    aio->queueForDeletion();
+    queuedDelete = true;
+    if (!callbacksInProgress)
+        delete this;
+}
+
+void SslAsynchIO::start(qpid::sys::Poller::shared_ptr poller) {
+    aio->start(poller);
+    startNegotiate();
+}
+
+void SslAsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) {
+    aio->queueReadBuffer(buff);
+}
+
+void SslAsynchIO::unread(AsynchIO::BufferBase* buff) {
+    // This is plaintext data being given back for more. Since it's already
+    // decrypted, don't give it back to the aio layer; keep it to append
+    // any new data for the upper layer.
+    assert(buff);
+    buff->squish();
+    assert(leftoverPlaintext == 0);
+    leftoverPlaintext = buff;
+}
+
+void SslAsynchIO::queueWrite(AsynchIO::BufferBase* buff) {
+    // @@TODO: Need to delay the write if the session is renegotiating.
+
+    // Should not have gotten here without an SslIoBuff. This assert is
+    // primarily to catch any stray cases where write is called with a buffer
+    // not obtained via getQueuedBuffer.
+    SslIoBuff *sslBuff = dynamic_cast<SslIoBuff*>(buff);
+    assert(sslBuff != 0);
+
+    // Encrypt and hand off to the io layer. Remember that the upper layer's
+    // encoding was working on, and adjustoing counts for, the SslIoBuff.
+    // Update the count of the original BufferBase before handing off to
+    // the I/O layer.
+    buff = sslBuff->release();
+    SecBuffer buffs[4];
+    buffs[0].cbBuffer = schSizes.cbHeader;
+    buffs[0].BufferType = SECBUFFER_STREAM_HEADER;
+    buffs[0].pvBuffer = buff->bytes;        // This space was left by SslIoBuff
+    buffs[1].cbBuffer = sslBuff->dataCount;
+    buffs[1].BufferType = SECBUFFER_DATA;
+    buffs[1].pvBuffer = sslBuff->bytes;
+    buffs[2].cbBuffer = schSizes.cbTrailer;
+    buffs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+    buffs[2].pvBuffer = &sslBuff->bytes[sslBuff->dataCount];
+    buffs[3].cbBuffer = 0;
+    buffs[3].BufferType = SECBUFFER_EMPTY;
+    buffs[3].pvBuffer = 0;
+    SecBufferDesc buffDesc;
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+    buffDesc.cBuffers = 4;
+    buffDesc.pBuffers = buffs;
+    SECURITY_STATUS status = ::EncryptMessage(&ctxtHandle, 0, &buffDesc, 0);
+
+    // EncryptMessage encrypts the data in place. The header and trailer
+    // areas were left previously and must now be included in the updated
+    // count of bytes to write to the peer.
+    delete sslBuff;
+    buff->dataCount = buffs[0].cbBuffer + buffs[1].cbBuffer + buffs[2].cbBuffer;
+    aio->queueWrite(buff);
+}
+
+void SslAsynchIO::notifyPendingWrite() {
+    aio->notifyPendingWrite();
+}
+
+void SslAsynchIO::queueWriteClose() {
+    if (state == Negotiating) {
+        // Never got going, so don't bother trying to close SSL down orderly.
+        state = ShuttingDown;
+        aio->queueWriteClose();
+        return;
+    }
+
+    state = ShuttingDown;
+
+    DWORD shutdown = SCHANNEL_SHUTDOWN;
+    SecBuffer shutBuff;
+    shutBuff.cbBuffer = sizeof(DWORD);
+    shutBuff.BufferType = SECBUFFER_TOKEN;
+    shutBuff.pvBuffer = &shutdown;
+    SecBufferDesc desc;
+    desc.ulVersion = SECBUFFER_VERSION;
+    desc.cBuffers = 1;
+    desc.pBuffers = &shutBuff;
+    ::ApplyControlToken(&ctxtHandle, &desc);
+    negotiateStep(0);
+    // When the shutdown sequence is done, negotiateDone() will handle
+    // shutting down aio.
+}
+
+bool SslAsynchIO::writeQueueEmpty() {
+    return aio->writeQueueEmpty();
+}
+
+/*
+ * Initiate a read operation. AsynchIO::readComplete() will be
+ * called when the read is complete and data is available.
+ */
+void SslAsynchIO::startReading() {
+    aio->startReading();
+}
+
+void SslAsynchIO::stopReading() {
+    aio->stopReading();
+}
+
+// Queue the specified callback for invocation from an I/O thread.
+void SslAsynchIO::requestCallback(RequestCallback callback) {
+    aio->requestCallback(callback);
+}
+
+/**
+ * Return a queued buffer read to put new data in for writing.
+ * This method ALWAYS returns a SslIoBuff reflecting a BufferBase from
+ * the aio layer that has header and trailer space reserved.
+ */
+AsynchIO::BufferBase* SslAsynchIO::getQueuedBuffer() {
+    SslIoBuff *sslBuff = 0;
+    BufferBase* buff = aio->getQueuedBuffer();
+    if (buff == 0)
+        return 0;
+
+    sslBuff = new SslIoBuff(buff, schSizes);
+    return sslBuff;
+}
+
+unsigned int SslAsynchIO::getSslKeySize() {
+    SecPkgContext_KeyInfo info;
+    memset(&info, 0, sizeof(info));
+    ::QueryContextAttributes(&ctxtHandle, SECPKG_ATTR_KEY_INFO, &info);
+    return info.KeySize;
+}
+
+void SslAsynchIO::negotiationDone() {
+    switch(state) {
+    case Negotiating:
+        ::QueryContextAttributes(&ctxtHandle,
+                                 SECPKG_ATTR_STREAM_SIZES,
+                                 &schSizes);
+        state = Running;
+        if (negotiateDoneCallback)
+            negotiateDoneCallback(SEC_E_OK);
+        break;
+    case Redo:
+        state = Running;
+        break;
+    case ShuttingDown:
+        aio->queueWriteClose();
+        break;
+    default:
+        assert(0);
+    }
+}
+
+void SslAsynchIO::negotiationFailed(SECURITY_STATUS status) {
+    QPID_LOG(notice, "SSL negotiation failed to " << peerAddress << ": " <<
+                     qpid::sys::strError(status));
+    if (negotiateDoneCallback)
+        negotiateDoneCallback(status);
+    else
+        queueWriteClose();
+}
+
+void SslAsynchIO::sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff) {
+    if (state != Running) {
+        negotiateStep(buff);
+        return;
+    }
+
+    // Decrypt the buffer; if there's legit data, pass it on through.
+    // However, it's also possible that the peer hasn't supplied enough
+    // data yet, or the session needs to be renegotiated, or the session
+    // is ending.
+    SecBuffer recvBuffs[4];
+    recvBuffs[0].cbBuffer = buff->dataCount;
+    recvBuffs[0].BufferType = SECBUFFER_DATA;
+    recvBuffs[0].pvBuffer = &buff->bytes[buff->dataStart];
+    recvBuffs[1].BufferType = SECBUFFER_EMPTY;
+    recvBuffs[2].BufferType = SECBUFFER_EMPTY;
+    recvBuffs[3].BufferType = SECBUFFER_EMPTY;
+    SecBufferDesc buffDesc;
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+    buffDesc.cBuffers = 4;
+    buffDesc.pBuffers = recvBuffs;
+    SECURITY_STATUS status = ::DecryptMessage(&ctxtHandle, &buffDesc, 0, NULL);
+    if (status != SEC_E_OK) {
+        if (status == SEC_E_INCOMPLETE_MESSAGE) {
+            // Give the partially filled buffer back and get more data
+            a.unread(buff);
+        }
+        else {
+            // Don't need this any more...
+            a.queueReadBuffer(buff);
+
+            if (status == SEC_I_RENEGOTIATE) {
+                state = Redo;
+                negotiateStep(0);
+            }
+            else if (status == SEC_I_CONTEXT_EXPIRED) {
+                queueWriteClose();
+            }
+            else {
+                throw QPID_WINDOWS_ERROR(status);
+            }
+        }
+        return;
+    }
+
+    // All decrypted and verified... continue with AMQP. The recvBuffs have
+    // been set up by DecryptMessage to demarcate the SSL header, data, and
+    // trailer, as well as any extra data left over. Walk through and find
+    // that info, adjusting the buff data accordingly to reflect only the
+    // actual decrypted data.
+    // If there's extra data, copy that out to a new buffer and run through
+    // this method again.
+    BufferBase *extraBuff = 0;
+    for (int i = 0; i < 4; i++) {
+        switch (recvBuffs[i].BufferType) {
+        case SECBUFFER_STREAM_HEADER:
+            buff->dataStart += recvBuffs[i].cbBuffer;
+            // Fall through - also don't count these bytes as data
+        case SECBUFFER_STREAM_TRAILER:
+            buff->dataCount -= recvBuffs[i].cbBuffer;
+            break;
+        case SECBUFFER_EXTRA:
+            extraBuff = getQueuedBuffer();
+            memmove(extraBuff->bytes,
+                    recvBuffs[i].pvBuffer,
+                    recvBuffs[i].cbBuffer);
+            extraBuff->dataCount = recvBuffs[i].cbBuffer;
+            break;
+        default:
+            break;
+        }
+    }
+
+    // Since we've already taken (possibly) all the available bytes from the
+    // aio layer, need to be sure that everything that's processable is
+    // processed before returning back to aio. It could be that any
+    // leftoverPlaintext data plus new buff data won't fit in one buffer, so
+    // need to keep going around the input processing loop until either
+    // all the bytes are gone, or there's less than a full frame remaining
+    // (so we can count on more bytes being on the way via aio).
+    do {
+        BufferBase *temp = 0;
+        // Now that buff reflects only decrypted data, see if there was any
+        // partial data left over from last time. If so, append this new
+        // data to that and release the current buff back to aio. Assume that
+        // leftoverPlaintext was squished so the data starts at 0.
+        if (leftoverPlaintext != 0) {
+            // There is leftover data; append all the new data that will fit.
+            int32_t count = buff->dataCount;
+            if (leftoverPlaintext->dataCount + count > leftoverPlaintext->byteCount)
+                count = (leftoverPlaintext->byteCount - leftoverPlaintext->dataCount);
+            ::memmove(&leftoverPlaintext->bytes[leftoverPlaintext->dataCount],
+                      &buff->bytes[buff->dataStart],
+                      count);
+            leftoverPlaintext->dataCount += count;
+            buff->dataCount -= count;
+            buff->dataStart += count;
+            if (buff->dataCount == 0) {
+                a.queueReadBuffer(buff);
+                buff = 0;
+            }
+            // Prepare to pass the buffer up. Beware that the read callback
+            // may do an unread(), so move the leftoverPlaintext pointer
+            // out of the way. It also may release the buffer back to aio,
+            // so in either event, the pointer passed to the callback is not
+            // valid on return.
+            temp = leftoverPlaintext;
+            leftoverPlaintext = 0;
+        }
+        else {
+            temp = buff;
+            buff = 0;
+        }
+        if (readCallback) {
+            // The callback guard here is to prevent an upcall from deleting
+            // this out from under us via queueForDeletion().
+            ++callbacksInProgress;
+            readCallback(*this, temp);
+            --callbacksInProgress;
+        }
+        else
+            a.queueReadBuffer(temp); // What else can we do with this???
+    } while (buff != 0);
+
+    // Ok, the current decrypted data is done. If there was any extra data,
+    // go back and handle that.
+    if (extraBuff != 0)
+        sslDataIn(a, extraBuff);
+
+    // If the upper layer queued for delete, do that now that all the
+    // callbacks are done.
+    if (queuedDelete && callbacksInProgress == 0)
+        delete this;
+}
+
+void SslAsynchIO::idle(qpid::sys::AsynchIO&) {
+    // Don't relay idle indication to layer above until SSL session is up.
+    if (state == Running) {
+        state = Running;
+        if (idleCallback)
+            idleCallback(*this);
+    }
+}
+
+  /**************************************************/
+
+ClientSslAsynchIO::ClientSslAsynchIO(const std::string& brokerHost,
+                                     const qpid::sys::Socket& s,
+                                     CredHandle hCred,
+                                     ReadCallback rCb,
+                                     EofCallback eofCb,
+                                     DisconnectCallback disCb,
+                                     ClosedCallback cCb,
+                                     BuffersEmptyCallback eCb,
+                                     IdleCallback iCb,
+                                     NegotiateDoneCallback nCb) :
+    SslAsynchIO(s, hCred, rCb, eofCb, disCb, cCb, eCb, iCb, nCb),
+    serverHost(brokerHost)
+{
+}
+
+void ClientSslAsynchIO::startNegotiate() {
+    // SEC_CHAR is non-const, so do all the typing here.
+    SEC_CHAR *host = const_cast<SEC_CHAR *>(serverHost.c_str());
+
+    // Need a buffer to receive the token to send to the server.
+    BufferBase *buff = aio->getQueuedBuffer();
+    ULONG ctxtRequested = ISC_REQ_STREAM;
+    ULONG ctxtAttrs;
+    // sendBuffs gets information to forward to the peer.
+    SecBuffer sendBuffs[2];
+    sendBuffs[0].cbBuffer = buff->byteCount;
+    sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+    sendBuffs[0].pvBuffer = buff->bytes;
+    sendBuffs[1].cbBuffer = 0;
+    sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+    sendBuffs[1].pvBuffer = 0;
+    SecBufferDesc sendBuffDesc;
+    sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+    sendBuffDesc.cBuffers = 2;
+    sendBuffDesc.pBuffers = sendBuffs;
+    SECURITY_STATUS status = ::InitializeSecurityContext(&credHandle,
+                                                         NULL,
+                                                         host,
+                                                         ctxtRequested,
+                                                         0,
+                                                         0,
+                                                         NULL,
+                                                         0,
+                                                         &ctxtHandle,
+                                                         &sendBuffDesc,
+                                                         &ctxtAttrs,
+                                                         NULL);
+    if (status == SEC_I_CONTINUE_NEEDED) {
+        buff->dataCount = sendBuffs[0].cbBuffer;
+        aio->queueWrite(buff);
+    }
+}
+
+void ClientSslAsynchIO::negotiateStep(BufferBase* buff) {
+    // SEC_CHAR is non-const, so do all the typing here.
+    SEC_CHAR *host = const_cast<SEC_CHAR *>(serverHost.c_str());
+    ULONG ctxtRequested = ISC_REQ_STREAM;
+    ULONG ctxtAttrs;
+
+    // tokenBuffs describe the buffer that's coming in. It should have
+    // a token from the SSL server.
+    SecBuffer tokenBuffs[2];
+    tokenBuffs[0].cbBuffer = buff ? buff->dataCount : 0;
+    tokenBuffs[0].BufferType = SECBUFFER_TOKEN;
+    tokenBuffs[0].pvBuffer = buff ? buff->bytes : 0;
+    tokenBuffs[1].cbBuffer = 0;
+    tokenBuffs[1].BufferType = SECBUFFER_EMPTY;
+    tokenBuffs[1].pvBuffer = 0;
+    SecBufferDesc tokenBuffDesc;
+    tokenBuffDesc.ulVersion = SECBUFFER_VERSION;
+    tokenBuffDesc.cBuffers = 2;
+    tokenBuffDesc.pBuffers = tokenBuffs;
+
+    // Need a buffer to receive any token to send back to the server.
+    BufferBase *sendbuff = aio->getQueuedBuffer();
+    // sendBuffs gets information to forward to the peer.
+    SecBuffer sendBuffs[2];
+    sendBuffs[0].cbBuffer = sendbuff->byteCount;
+    sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+    sendBuffs[0].pvBuffer = sendbuff->bytes;
+    sendBuffs[1].cbBuffer = 0;
+    sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+    sendBuffs[1].pvBuffer = 0;
+    SecBufferDesc sendBuffDesc;
+    sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+    sendBuffDesc.cBuffers = 2;
+    sendBuffDesc.pBuffers = sendBuffs;
+
+    SECURITY_STATUS status = ::InitializeSecurityContext(&credHandle,
+                                                         &ctxtHandle,
+                                                         host,
+                                                         ctxtRequested,
+                                                         0,
+                                                         0,
+                                                         &tokenBuffDesc,
+                                                         0,
+                                                         NULL,
+                                                         &sendBuffDesc,
+                                                         &ctxtAttrs,
+                                                         NULL);
+
+    if (status == SEC_E_INCOMPLETE_MESSAGE) {
+        // Not enough - get more data from the server then try again.
+        aio->unread(buff);
+        aio->queueReadBuffer(sendbuff);   // Don't need this one for now...
+        return;
+    }
+    // Done with the buffer that came in...
+    if (buff)
+        aio->queueReadBuffer(buff);
+    if (status == SEC_I_CONTINUE_NEEDED) {
+        sendbuff->dataCount = sendBuffs[0].cbBuffer;
+        aio->queueWrite(sendbuff);
+        return;
+    }
+    // Nothing to send back to the server...
+    aio->queueReadBuffer(sendbuff);
+    // SEC_I_CONTEXT_EXPIRED means session stop complete; SEC_E_OK can be
+    // either session stop or negotiation done (session up).
+    if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED)
+        negotiationDone();
+    else
+        negotiationFailed(status);
+}
+
+/*************************************************/
+
+ServerSslAsynchIO::ServerSslAsynchIO(bool clientMustAuthenticate,
+                                     const qpid::sys::Socket& s,
+                                     CredHandle hCred,
+                                     ReadCallback rCb,
+                                     EofCallback eofCb,
+                                     DisconnectCallback disCb,
+                                     ClosedCallback cCb,
+                                     BuffersEmptyCallback eCb,
+                                     IdleCallback iCb,
+                                     NegotiateDoneCallback nCb) :
+    SslAsynchIO(s, hCred, rCb, eofCb, disCb, cCb, eCb, iCb, nCb),
+    clientAuth(clientMustAuthenticate)
+{
+}
+
+void ServerSslAsynchIO::startNegotiate() {
+    // Nothing... need the client to send a token first.
+}
+
+void ServerSslAsynchIO::negotiateStep(BufferBase* buff) {
+    ULONG ctxtRequested = ASC_REQ_STREAM;
+    if (clientAuth)
+        ctxtRequested |= ASC_REQ_MUTUAL_AUTH;
+    ULONG ctxtAttrs;
+
+    // tokenBuffs describe the buffer that's coming in. It should have
+    // a token from the SSL server except if shutting down or renegotiating.
+    SecBuffer tokenBuffs[2];
+    tokenBuffs[0].cbBuffer = buff ? buff->dataCount : 0;
+    tokenBuffs[0].BufferType = SECBUFFER_TOKEN;
+    tokenBuffs[0].pvBuffer = buff ? buff->bytes : 0;
+    tokenBuffs[1].cbBuffer = 0;
+    tokenBuffs[1].BufferType = SECBUFFER_EMPTY;
+    tokenBuffs[1].pvBuffer = 0;
+    SecBufferDesc tokenBuffDesc;
+    tokenBuffDesc.ulVersion = SECBUFFER_VERSION;
+    tokenBuffDesc.cBuffers = 2;
+    tokenBuffDesc.pBuffers = tokenBuffs;
+
+    // Need a buffer to receive any token to send back to the server.
+    BufferBase *sendbuff = aio->getQueuedBuffer();
+    // sendBuffs gets information to forward to the peer.
+    SecBuffer sendBuffs[2];
+    sendBuffs[0].cbBuffer = sendbuff->byteCount;
+    sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+    sendBuffs[0].pvBuffer = sendbuff->bytes;
+    sendBuffs[1].cbBuffer = 0;
+    sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+    sendBuffs[1].pvBuffer = 0;
+    SecBufferDesc sendBuffDesc;
+    sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+    sendBuffDesc.cBuffers = 2;
+    sendBuffDesc.pBuffers = sendBuffs;
+    PCtxtHandle ctxtHandlePtr = (SecIsValidHandle(&ctxtHandle)) ? &ctxtHandle : 0;
+    SECURITY_STATUS status = ::AcceptSecurityContext(&credHandle,
+                                                     ctxtHandlePtr,
+                                                     &tokenBuffDesc,
+                                                     ctxtRequested,
+                                                     0,
+                                                     &ctxtHandle,
+                                                     &sendBuffDesc,
+                                                     &ctxtAttrs,
+                                                     NULL);
+    if (status == SEC_E_INCOMPLETE_MESSAGE) {
+        // Not enough - get more data from the server then try again.
+        if (buff)
+            aio->unread(buff);
+        aio->queueReadBuffer(sendbuff);   // Don't need this one for now...
+        return;
+    }
+    // Done with the buffer that came in...
+    if (buff)
+        aio->queueReadBuffer(buff);
+    if (status == SEC_I_CONTINUE_NEEDED) {
+        sendbuff->dataCount = sendBuffs[0].cbBuffer;
+        aio->queueWrite(sendbuff);
+        return;
+    }
+    // There may have been a token generated; if so, send it to the client.
+    if (sendBuffs[0].cbBuffer > 0) {
+        sendbuff->dataCount = sendBuffs[0].cbBuffer;
+        aio->queueWrite(sendbuff);
+    }
+    else
+        // Nothing to send back to the server...
+        aio->queueReadBuffer(sendbuff);
+
+    // SEC_I_CONTEXT_EXPIRED means session stop complete; SEC_E_OK can be
+    // either session stop or negotiation done (session up).
+    if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
+        if (clientAuth)
+            QPID_LOG(warning, "DID WE CHECK FOR CLIENT AUTH???");
+
+        negotiationDone();
+    }
+    else {
+        negotiationFailed(status);
+    }
+}
+
+}}}  // namespace qpid::sys::windows

Added: qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h?rev=902318&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h (added)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h Sat Jan 23 00:16:34 2010
@@ -0,0 +1,191 @@
+#ifndef _sys_windows_SslAsynchIO
+#define _sys_windows_SslAsynchIO
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <windows.h>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+namespace qpid {
+namespace sys {
+namespace windows {
+    
+class Socket;
+class Poller;
+
+/*
+ * SSL/Schannel shim between the frame-handling and AsynchIO layers.
+ * SslAsynchIO creates a regular AsynchIO object to handle I/O and this class
+ * gets involved for SSL negotiations and encrypt/decrypt. The details of
+ * how this all works are invisible to the layers on either side. The only
+ * change from normal AsynchIO usage is that there's an extra callback
+ * from SslAsynchIO to indicate that the initial session negotiation is
+ * complete.
+ *
+ * The details of session negotiation are different for client and server
+ * SSL roles. These differences are handled by deriving separate client
+ * and server role classes.
+ */
+class SslAsynchIO : public qpid::sys::AsynchIO {
+public:
+    typedef boost::function1<void, SECURITY_STATUS> NegotiateDoneCallback;
+
+    SslAsynchIO(const qpid::sys::Socket& s,
+                CredHandle hCred,
+                ReadCallback rCb,
+                EofCallback eofCb,
+                DisconnectCallback disCb,
+                ClosedCallback cCb = 0,
+                BuffersEmptyCallback eCb = 0,
+                IdleCallback iCb = 0,
+                NegotiateDoneCallback nCb = 0);
+    ~SslAsynchIO();
+
+    virtual void queueForDeletion();
+
+    virtual void start(qpid::sys::Poller::shared_ptr poller);
+    virtual void queueReadBuffer(BufferBase* buff);
+    virtual void unread(BufferBase* buff);
+    virtual void queueWrite(BufferBase* buff);
+    virtual void notifyPendingWrite();
+    virtual void queueWriteClose();
+    virtual bool writeQueueEmpty();
+    virtual void startReading();
+    virtual void stopReading();
+    virtual void requestCallback(RequestCallback);
+    virtual BufferBase* getQueuedBuffer();
+
+    QPID_COMMON_EXTERN unsigned int getSslKeySize();
+
+protected:
+    CredHandle credHandle;
+
+    // AsynchIO layer below that's actually doing the I/O
+    qpid::sys::AsynchIO *aio;
+
+    // Track what the state of the SSL session is. Have to know when it's
+    // time to notify the upper layer that the session is up, and also to
+    // know when it's not legit to pass data through to either side.
+    enum { Negotiating, Running, Redo, ShuttingDown } state;
+    bool sessionUp;
+    CtxtHandle ctxtHandle;
+    TimeStamp credExpiry;
+
+    // Client- and server-side SSL subclasses implement these to do the
+    // proper negotiation steps. negotiateStep() is called with a buffer
+    // just received from the peer.
+    virtual void startNegotiate() = 0;
+    virtual void negotiateStep(BufferBase *buff) = 0;
+
+    // The negotiating steps call one of these when it's finalized:
+    void negotiationDone();
+    void negotiationFailed(SECURITY_STATUS status);
+
+private:
+    // These are callbacks from AsynchIO to here.
+    void sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff);
+    void idle(qpid::sys::AsynchIO&);
+
+    // These callbacks are to the layer above.
+    ReadCallback readCallback;
+    IdleCallback idleCallback;
+    NegotiateDoneCallback negotiateDoneCallback;
+    volatile unsigned int callbacksInProgress;   // >0 if w/in callbacks
+    volatile bool queuedDelete;
+
+    // Address of peer, in case it's needed for logging.
+    std::string peerAddress;
+
+    // Partial buffer of decrypted plaintext given back by the layer above.
+    AsynchIO::BufferBase *leftoverPlaintext;
+
+    SecPkgContext_StreamSizes schSizes;
+};
+
+/*
+ * SSL/Schannel client-side shim between the frame-handling and AsynchIO
+ * layers.
+ */
+class ClientSslAsynchIO : public SslAsynchIO {
+public:
+    // Args same as for SslIoShim, with the addition of brokerHost which is
+    // the expected SSL name of the server.
+    QPID_COMMON_EXTERN ClientSslAsynchIO(const std::string& brokerHost,
+                                         const qpid::sys::Socket& s,
+                                         CredHandle hCred,
+                                         ReadCallback rCb,
+                                         EofCallback eofCb,
+                                         DisconnectCallback disCb,
+                                         ClosedCallback cCb = 0,
+                                         BuffersEmptyCallback eCb = 0,
+                                         IdleCallback iCb = 0,
+                                         NegotiateDoneCallback nCb = 0);
+
+private:
+    std::string serverHost;
+
+    // Client- and server-side SSL subclasses implement these to do the
+    // proper negotiation steps. negotiateStep() is called with a buffer
+    // just received from the peer.
+    void startNegotiate();
+    void negotiateStep(BufferBase *buff);
+};
+/*
+ * SSL/Schannel server-side shim between the frame-handling and AsynchIO
+ * layers.
+ */
+class ServerSslAsynchIO : public SslAsynchIO {
+public:
+    QPID_COMMON_EXTERN ServerSslAsynchIO(bool clientMustAuthenticate,
+                                         const qpid::sys::Socket& s,
+                                         CredHandle hCred,
+                                         ReadCallback rCb,
+                                         EofCallback eofCb,
+                                         DisconnectCallback disCb,
+                                         ClosedCallback cCb = 0,
+                                         BuffersEmptyCallback eCb = 0,
+                                         IdleCallback iCb = 0,
+                                         NegotiateDoneCallback nCb = 0);
+
+private:
+    bool clientAuth;
+
+    // Client- and server-side SSL subclasses implement these to do the
+    // proper negotiation steps. negotiateStep() is called with a buffer
+    // just received from the peer.
+    void startNegotiate();
+    void negotiateStep(BufferBase *buff);
+};
+
+}}}   // namespace qpid::sys::windows
+
+#endif // _sys_windows_SslAsynchIO

Modified: qpid/trunk/qpid/cpp/src/ssl.cmake
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/ssl.cmake?rev=902318&r1=902317&r2=902318&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/ssl.cmake (original)
+++ qpid/trunk/qpid/cpp/src/ssl.cmake Sat Jan 23 00:16:34 2010
@@ -31,6 +31,7 @@
 
 set (ssl_default ${ssl_force})
 if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+  set (ssl_default ON)
 else (CMAKE_SYSTEM_NAME STREQUAL Windows)
   if (NSS_FOUND)
     set (ssl_default ON)
@@ -39,68 +40,78 @@
 
 option(BUILD_SSL "Build with support for SSL" ${ssl_default})
 if (BUILD_SSL)
-
-  if (NOT NSS_FOUND)
-    message(FATAL_ERROR "nss/nspr not found, required for ssl support")
-  endif (NOT NSS_FOUND)
-
-  foreach(f ${NSS_CFLAGS})
-    set (NSS_COMPILE_FLAGS "${NSS_COMPILE_FLAGS} ${f}")
-  endforeach(f)
-
-  foreach(f ${NSS_LDFLAGS})
-    set (NSS_LINK_FLAGS "${NSS_LINK_FLAGS} ${f}")
-  endforeach(f)
-
-  set (sslcommon_SOURCES
-       qpid/sys/ssl/check.h
-       qpid/sys/ssl/check.cpp
-       qpid/sys/ssl/util.h
-       qpid/sys/ssl/util.cpp
-       qpid/sys/ssl/SslSocket.h
-       qpid/sys/ssl/SslSocket.cpp
-       qpid/sys/ssl/SslIo.h
-       qpid/sys/ssl/SslIo.cpp
-      )
-
-  add_library (sslcommon SHARED ${sslcommon_SOURCES})
-  target_link_libraries (sslcommon qpidcommon)
-  set_target_properties (sslcommon PROPERTIES
-                         VERSION ${qpidc_version}
-                         COMPILE_FLAGS ${NSS_COMPILE_FLAGS}
-                         LINK_FLAGS ${NSS_LINK_FLAGS})
-
-  set (ssl_SOURCES
-       qpid/sys/SslPlugin.cpp
-       qpid/sys/ssl/SslHandler.h
-       qpid/sys/ssl/SslHandler.cpp
-      )
-  add_library (ssl MODULE ${ssl_SOURCES})
-  target_link_libraries (ssl qpidbroker sslcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
-  set_target_properties (ssl PROPERTIES
-                         PREFIX ""
-                         COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    set_target_properties(ssl PROPERTIES
-                          LINK_FLAGS -Wl,--no-undefined)
-  endif (CMAKE_COMPILER_IS_GNUCXX)
-
-  install (TARGETS ssl
-           DESTINATION ${QPIDD_MODULE_DIR}
-           COMPONENT ${QPID_COMPONENT_BROKER})
-
-  add_library (sslconnector MODULE qpid/client/SslConnector.cpp)
-  target_link_libraries (sslconnector qpidclient sslcommon)
-  set_target_properties (sslconnector PROPERTIES
-                         PREFIX ""
-                         COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
-  if (CMAKE_COMPILER_IS_GNUCXX)
-    set_target_properties(sslconnector PROPERTIES
-                          LINK_FLAGS -Wl,--no-undefined)
-  endif (CMAKE_COMPILER_IS_GNUCXX)
-
-  install (TARGETS sslconnector
-           DESTINATION ${QPIDC_MODULE_DIR}
-           COMPONENT ${QPID_COMPONENT_CLIENT})
+  if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+    set (sslclient_windows_SOURCES qpid/client/windows/SslConnector.cpp)
+    set (sslbroker_windows_SOURCES qpid/broker/windows/SslProtocolFactory.cpp)
+    set (sslcommon_windows_SOURCES
+         qpid/sys/windows/SslAsynchIO.cpp
+        )
+    set (windows_ssl_libs Secur32.lib)
+    set (windows_ssl_server_libs Crypt32.lib)
+  else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+    if (NOT NSS_FOUND)
+      message(FATAL_ERROR "nss/nspr not found, required for ssl support")
+    endif (NOT NSS_FOUND)
+
+    foreach(f ${NSS_CFLAGS})
+      set (NSS_COMPILE_FLAGS "${NSS_COMPILE_FLAGS} ${f}")
+    endforeach(f)
+
+    foreach(f ${NSS_LDFLAGS})
+      set (NSS_LINK_FLAGS "${NSS_LINK_FLAGS} ${f}")
+    endforeach(f)
+
+    set (sslcommon_SOURCES
+         qpid/sys/ssl/check.h
+         qpid/sys/ssl/check.cpp
+         qpid/sys/ssl/util.h
+         qpid/sys/ssl/util.cpp
+         qpid/sys/ssl/SslSocket.h
+         qpid/sys/ssl/SslSocket.cpp
+         qpid/sys/ssl/SslIo.h
+         qpid/sys/ssl/SslIo.cpp
+        )
+
+    add_library (sslcommon SHARED ${sslcommon_SOURCES})
+    target_link_libraries (sslcommon qpidcommon)
+    set_target_properties (sslcommon PROPERTIES
+                           VERSION ${qpidc_version}
+                           COMPILE_FLAGS ${NSS_COMPILE_FLAGS}
+                           LINK_FLAGS ${NSS_LINK_FLAGS})
+
+    set (ssl_SOURCES
+         qpid/sys/SslPlugin.cpp
+         qpid/sys/ssl/SslHandler.h
+         qpid/sys/ssl/SslHandler.cpp
+        )
+    add_library (ssl MODULE ${ssl_SOURCES})
+    target_link_libraries (ssl qpidbroker sslcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
+    set_target_properties (ssl PROPERTIES
+                           PREFIX ""
+                           COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
+    if (CMAKE_COMPILER_IS_GNUCXX)
+      set_target_properties(ssl PROPERTIES
+                            LINK_FLAGS -Wl,--no-undefined)
+    endif (CMAKE_COMPILER_IS_GNUCXX)
+
+    install (TARGETS ssl
+             DESTINATION ${QPIDD_MODULE_DIR}
+             COMPONENT ${QPID_COMPONENT_BROKER})
+
+    add_library (sslconnector MODULE qpid/client/SslConnector.cpp)
+    target_link_libraries (sslconnector qpidclient sslcommon)
+    set_target_properties (sslconnector PROPERTIES
+                           PREFIX ""
+                           COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
+    if (CMAKE_COMPILER_IS_GNUCXX)
+      set_target_properties(sslconnector PROPERTIES
+                            LINK_FLAGS -Wl,--no-undefined)
+    endif (CMAKE_COMPILER_IS_GNUCXX)
+
+    install (TARGETS sslconnector
+             DESTINATION ${QPIDC_MODULE_DIR}
+             COMPONENT ${QPID_COMPONENT_CLIENT})
+  endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
 
 endif (BUILD_SSL)



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org