You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2013/01/08 09:29:32 UTC

svn commit: r1430167 [2/3] - in /qpid/proton/branches/jni-binding: ./ bin/ proton-c/ proton-c/bindings/perl/ proton-c/bindings/php/ proton-c/bindings/python/ proton-c/bindings/ruby/ proton-c/bindings/ruby/lib/qpid_proton/ proton-c/include/proton/ proto...

Modified: qpid/proton/branches/jni-binding/proton-c/src/ssl/openssl.c
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-c/src/ssl/openssl.c?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-c/src/ssl/openssl.c (original)
+++ qpid/proton/branches/jni-binding/proton-c/src/ssl/openssl.c Tue Jan  8 08:29:31 2013
@@ -23,11 +23,13 @@
 #include "./ssl-internal.h"
 #include <proton/engine.h>
 #include "../engine/engine-internal.h"
+#include "../platform.h"
 #include "../util.h"
 
 #include <openssl/ssl.h>
 #include <openssl/dh.h>
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -41,21 +43,39 @@
  */
 
 static int ssl_initialized;
+static int ssl_ex_data_index;
 
 typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION } connection_mode_t;
+typedef struct pn_ssl_session_t pn_ssl_session_t;
 
-struct pn_ssl_t {
-  SSL_CTX *ctx;
-  SSL *ssl;
+struct pn_ssl_domain_t {
+  int   ref_count;
+
+  SSL_CTX       *ctx;
   pn_ssl_mode_t mode;
-  bool allow_unsecured; // allow non-SSL connections
+
   bool has_ca_db;       // true when CA database configured
   bool has_certificate; // true when certificate configured
   char *keyfile_pw;
-  pn_ssl_verify_mode_t verify_mode;
+
+  // settings used for all connections
   char *trusted_CAs;
+  pn_ssl_verify_mode_t verify_mode;
+  bool allow_unsecured;
+
+  // session cache
+  pn_ssl_session_t *ssn_cache_head;
+  pn_ssl_session_t *ssn_cache_tail;
+};
+
+
+struct pn_ssl_t {
 
-  pn_transport_t *transport;
+  pn_transport_t   *transport;
+  pn_ssl_domain_t  *domain;
+  const char    *session_id;
+  const char *peer_hostname;
+  SSL *ssl;
 
   BIO *bio_ssl;         // i/o from/to SSL socket layer
   BIO *bio_ssl_io;      // SSL "half" of network-facing BIO
@@ -82,6 +102,14 @@ struct pn_ssl_t {
   pn_trace_t trace;
 };
 
+struct pn_ssl_session_t {
+  const char       *id;
+  SSL_SESSION      *session;
+  pn_ssl_session_t *ssn_cache_next;
+  pn_ssl_session_t *ssn_cache_prev;
+};
+
+
 // define two sets of allowable ciphers: those that require authentication, and those
 // that do not require authentication (anonymous).  See ciphers(1).
 #define CIPHERS_AUTHENTICATE    "ALL:!aNULL:!eNULL:@STRENGTH"
@@ -97,6 +125,9 @@ static ssize_t process_input_unknown(pn_
 static ssize_t process_output_unknown(pn_transport_t *transport, char *input_data, size_t len);
 static connection_mode_t check_for_ssl_connection( const char *data, size_t len );
 static int init_ssl_socket( pn_ssl_t * );
+static void release_ssl_socket( pn_ssl_t * );
+static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *, const char * );
+static void ssl_session_free( pn_ssl_session_t *);
 
 
 // @todo: used to avoid littering the code with calls to printf...
@@ -120,7 +151,7 @@ static void _log(pn_ssl_t *ssl, const ch
 }
 
 // log an error and dump the SSL error stack
-static void _log_ssl_error(pn_ssl_t *ssl, const char *fmt, ...)
+static void _log_ssl_error( const char *fmt, ...)
 {
   char buf[128];        // see "man ERR_error_string_n()"
   va_list ap;
@@ -159,59 +190,148 @@ static int ssl_failed(pn_ssl_t *ssl)
   if (ssl_err) {
     ERR_error_string_n( ssl_err, buf, sizeof(buf) );
   }
-  _log_ssl_error(ssl, NULL);    // spit out any remaining errors to the log file
+  _log_ssl_error(NULL);    // spit out any remaining errors to the log file
   return pn_error_format( ssl->transport->error, PN_ERR, "SSL Failure: %s", buf );
 }
 
-// @todo replace with a "reasonable" default (?), allow application to register its own
-// callback.
-#if 0
+/* match the DNS name pattern from the peer certificate against our configured peer
+   hostname */
+static bool match_dns_pattern( const char *hostname,
+                               const char *pattern, int plen )
+{
+
+  if (memchr( pattern, '*', plen ) == NULL)
+    return (plen == strlen(hostname) &&
+            strncasecmp( pattern, hostname, plen ) == 0);
+
+  /* dns wildcarded pattern - RFC2818 */
+  char plabel[64];   /* max label length < 63 - RFC1034 */
+  char slabel[64];
+  int slen = strlen(hostname);
+
+  while (plen > 0 && slen > 0) {
+    const char *cptr;
+    int len;
+
+    cptr = memchr( pattern, '.', plen );
+    len = (cptr) ? cptr - pattern : plen;
+    if (len > sizeof(plabel) - 1) return false;
+    memcpy( plabel, pattern, len );
+    plabel[len] = 0;
+    pattern = cptr + 1;
+    plen -= len;
+
+    cptr = memchr( hostname, '.', slen );
+    len = (cptr) ? cptr - hostname : slen;
+    if (len > sizeof(slabel) - 1) return false;
+    memcpy( slabel, hostname, len );
+    slabel[len] = 0;
+    hostname = cptr + 1;
+    slen -= len;
+
+    char *star = strchr( plabel, '*' );
+    if (!star) {
+      if (strcasecmp( plabel, slabel )) return false;
+    } else {
+      *star = '\0';
+      char *prefix = plabel;
+      int prefix_len = strlen(prefix);
+      char *suffix = star + 1;
+      int suffix_len = strlen(suffix);
+      if (prefix_len && strncasecmp( prefix, slabel, prefix_len )) return false;
+      if (suffix_len && strncasecmp( suffix,
+                                     slabel + (strlen(slabel) - suffix_len),
+                                     suffix_len )) return false;
+    }
+  }
+
+  return plen == slen;
+}
+
+// Certificate chain verification callback: return 1 if verified,
+// 0 if remote cannot be verified (fail handshake).
+//
 static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
 {
-    fprintf(stderr, "VERIFY_CALLBACK: pre-verify-ok=%d\n", preverify_ok);
+  if (!preverify_ok || X509_STORE_CTX_get_error_depth(ctx) != 0)
+    // already failed, or not at peer cert in chain
+    return preverify_ok;
+
+  X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+  SSL *ssn = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+  if (!ssn) {
+    _log_error("Error: unexpected error - SSL session info not available for peer verify!\n");
+    return 0;  // fail connection
+  }
+
+  pn_ssl_t *ssl = (pn_ssl_t *)SSL_get_ex_data(ssn, ssl_ex_data_index);
+  if (!ssl) {
+    _log_error("Error: unexpected error - SSL context info not available for peer verify!\n");
+    return 0;  // fail connection
+  }
+
+  if (ssl->domain->verify_mode != PN_SSL_VERIFY_PEER_NAME) return preverify_ok;
+  if (!ssl->peer_hostname) {
+    _log_error("Error: configuration error: PN_SSL_VERIFY_PEER_NAME configured, but no peer hostname set!\n");
+    return 0;  // fail connection
+  }
+
+  _log( ssl, "Checking identifying name in peer cert against '%s'\n", ssl->peer_hostname);
+
+  bool matched = false;
+
+  /* first check any SubjectAltName entries, as per RFC2818 */
+  GENERAL_NAMES *sans = (GENERAL_NAMES *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+  if (sans) {
+    int name_ct = sk_GENERAL_NAME_num( sans );
+    int i;
+    for (i = 0; !matched && i < name_ct; ++i) {
+      GENERAL_NAME *name = sk_GENERAL_NAME_value( sans, i );
+      if (name->type == GEN_DNS) {
+        ASN1_STRING *asn1 = name->d.dNSName;
+        if (asn1 && asn1->data && asn1->length) {
+          unsigned char *str;
+          int len = ASN1_STRING_to_UTF8( &str, asn1 );
+          if (len >= 0) {
+            _log( ssl, "SubjectAltName (dns) from peer cert = '%.*s'\n", len, str );
+            matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
+            OPENSSL_free( str );
+          }
+        }
+      }
+    }
+    GENERAL_NAMES_free( sans );
+  }
 
-           char    buf[256];
-           X509   *err_cert;
-           int     err, depth;
-
-           err_cert = X509_STORE_CTX_get_current_cert(ctx);
-           err = X509_STORE_CTX_get_error(ctx);
-           depth = X509_STORE_CTX_get_error_depth(ctx);
-
-           /*
-            * Retrieve the pointer to the SSL of the connection currently treated
-            * and the application specific data stored into the SSL object.
-            */
-
-           X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
-
-           /*
-            * Catch a too long certificate chain. The depth limit set using
-            * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
-            * that whenever the "depth>verify_depth" condition is met, we
-            * have violated the limit and want to log this error condition.
-            * We must do it here, because the CHAIN_TOO_LONG error would not
-            * be found explicitly; only errors introduced by cutting off the
-            * additional certificates would be logged.
-            */
-           if (!preverify_ok) {
-               printf("verify error:num=%d:%s:depth=%d:%s\n", err,
-                        X509_verify_cert_error_string(err), depth, buf);
-           }
-
-           /*
-            * At this point, err contains the last verification error. We can use
-            * it for something special
-            */
-           if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
-           {
-             X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
-             printf("issuer= %s\n", buf);
-           }
+  /* if no general names match, try the CommonName from the subject */
+  X509_NAME *name = X509_get_subject_name(cert);
+  int i = -1;
+  while (!matched && (i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) {
+    X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i);
+    ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne);
+    if (name_asn1) {
+      unsigned char *str;
+      int len = ASN1_STRING_to_UTF8( &str, name_asn1);
+      if (len >= 0) {
+        _log( ssl, "commonName from peer cert = '%.*s'\n", len, str );
+        matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
+        OPENSSL_free(str);
+      }
+    }
+  }
 
-    return 1;
-}
+  if (!matched) {
+    _log( ssl, "Error: no name matching %s found in peer cert - rejecting handshake.\n",
+          ssl->peer_hostname);
+    preverify_ok = 0;
+#ifdef X509_V_ERR_APPLICATION_VERIFICATION
+    X509_STORE_CTX_set_error( ctx, X509_V_ERR_APPLICATION_VERIFICATION );
 #endif
+  } else {
+    _log( ssl, "Name from peer cert matched - peer is valid.\n" );
+  }
+  return preverify_ok;
+}
 
 
 // this code was generated using the command:
@@ -255,66 +375,174 @@ static DH *get_dh2048()
   return(dh);
 }
 
+static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *domain, const char *id )
+{
+  pn_timestamp_t now_msec = pn_i_now();
+  long now_sec = (long)(now_msec / 1000);
+  pn_ssl_session_t *ssn = LL_HEAD( domain, ssn_cache );
+  while (ssn) {
+    long expire = SSL_SESSION_get_time( ssn->session )
+      + SSL_SESSION_get_timeout( ssn->session );
+    if (expire < now_sec) {
+      pn_ssl_session_t *next = ssn->ssn_cache_next;
+      LL_REMOVE( domain, ssn_cache, ssn );
+      ssl_session_free( ssn );
+      ssn = next;
+      continue;
+    }
+
+    if (!strcmp(ssn->id, id)) {
+      break;
+    }
+    ssn = ssn->ssn_cache_next;
+  }
+  return ssn;
+}
+
+static void ssl_session_free( pn_ssl_session_t *ssn)
+{
+  if (ssn) {
+    if (ssn->id) free( (void *)ssn->id );
+    if (ssn->session) SSL_SESSION_free( ssn->session );
+    free( ssn );
+  }
+}
+
 
 /** Public API - visible to application code */
 
+pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode )
+{
+  if (!ssl_initialized) {
+    ssl_initialized = 1;
+    SSL_library_init();
+    SSL_load_error_strings();
+    OpenSSL_add_all_algorithms();
+    ssl_ex_data_index = SSL_get_ex_new_index( 0, "org.apache.qpid.proton.ssl",
+                                              NULL, NULL, NULL);
+  }
 
-int pn_ssl_set_credentials( pn_ssl_t *ssl,
-                            const char *certificate_file,
-                            const char *private_key_file,
-                            const char *password)
+  pn_ssl_domain_t *domain = calloc(1, sizeof(pn_ssl_domain_t));
+  if (!domain) return NULL;
+
+  domain->ref_count = 1;
+  domain->mode = mode;
+  switch(mode) {
+  case PN_SSL_MODE_CLIENT:
+    domain->ctx = SSL_CTX_new(TLSv1_client_method());
+    if (!domain->ctx) {
+      _log_ssl_error( "Unable to initialize OpenSSL context.\n");
+      free(domain);
+      return NULL;
+    }
+    break;
+
+  case PN_SSL_MODE_SERVER:
+    domain->ctx = SSL_CTX_new(SSLv23_server_method());
+    if (!domain->ctx) {
+      _log_ssl_error("Unable to initialize OpenSSL context.\n");
+      free(domain);
+      return NULL;
+    }
+    SSL_CTX_set_options(domain->ctx, SSL_OP_NO_SSLv2);  // v2 is insecure
+    break;
+
+  default:
+    _log_error("Invalid valid for pn_ssl_mode_t: %d\n", mode);
+    free(domain);
+    return NULL;
+  }
+
+  // by default, allow anonymous ciphers so certificates are not required 'out of the box'
+  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_ANONYMOUS )) {
+    _log_ssl_error("Failed to set cipher list to %s\n", CIPHERS_ANONYMOUS);
+    pn_ssl_domain_free(domain);
+    return NULL;
+  }
+
+  // ditto: by default do not authenticate the peer (can be done by SASL).
+  if (pn_ssl_domain_set_peer_authentication( domain, PN_SSL_ANONYMOUS_PEER, NULL )) {
+    pn_ssl_domain_free(domain);
+    return NULL;
+  }
+
+  DH *dh = get_dh2048();
+  if (dh) {
+    SSL_CTX_set_tmp_dh(domain->ctx, dh);
+    DH_free(dh);
+    SSL_CTX_set_options(domain->ctx, SSL_OP_SINGLE_DH_USE);
+  }
+
+  return domain;
+}
+
+void pn_ssl_domain_free( pn_ssl_domain_t *domain )
 {
-  if (!ssl) return -1;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set credentials while SSL in use.\n");
-    return -1;
+  if (--domain->ref_count == 0) {
+
+    pn_ssl_session_t *ssn = LL_HEAD( domain, ssn_cache );
+    while (ssn) {
+      pn_ssl_session_t *next = ssn->ssn_cache_next;
+      LL_REMOVE( domain, ssn_cache, ssn );
+      ssl_session_free( ssn );
+      ssn = next;
+    }
+
+    if (domain->ctx) SSL_CTX_free(domain->ctx);
+    if (domain->keyfile_pw) free(domain->keyfile_pw);
+    if (domain->trusted_CAs) free(domain->trusted_CAs);
+    free(domain);
   }
+}
 
-  if (SSL_CTX_use_certificate_chain_file(ssl->ctx, certificate_file) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_use_certificate_chain_file( %s ) failed\n", certificate_file);
+
+int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
+                               const char *certificate_file,
+                               const char *private_key_file,
+                               const char *password)
+{
+  if (!domain || !domain->ctx) return -1;
+
+  if (SSL_CTX_use_certificate_chain_file(domain->ctx, certificate_file) != 1) {
+    _log_ssl_error( "SSL_CTX_use_certificate_chain_file( %s ) failed\n", certificate_file);
     return -3;
   }
 
   if (password) {
-    ssl->keyfile_pw = pn_strdup(password);  // @todo: obfuscate me!!!
-    SSL_CTX_set_default_passwd_cb(ssl->ctx, keyfile_pw_cb);
-    SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ssl->keyfile_pw);
+    domain->keyfile_pw = pn_strdup(password);  // @todo: obfuscate me!!!
+    SSL_CTX_set_default_passwd_cb(domain->ctx, keyfile_pw_cb);
+    SSL_CTX_set_default_passwd_cb_userdata(domain->ctx, domain->keyfile_pw);
   }
 
-  if (SSL_CTX_use_PrivateKey_file(ssl->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_use_PrivateKey_file( %s ) failed\n", private_key_file);
+  if (SSL_CTX_use_PrivateKey_file(domain->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) {
+    _log_ssl_error( "SSL_CTX_use_PrivateKey_file( %s ) failed\n", private_key_file);
     return -4;
   }
 
-  if (SSL_CTX_check_private_key(ssl->ctx) != 1) {
-    _log_ssl_error(ssl, "The key file %s is not consistent with the certificate %s\n",
+  if (SSL_CTX_check_private_key(domain->ctx) != 1) {
+    _log_ssl_error( "The key file %s is not consistent with the certificate %s\n",
                    private_key_file, certificate_file);
     return -5;
   }
 
-  ssl->has_certificate = true;
+  domain->has_certificate = true;
 
   // bug in older versions of OpenSSL: servers may request client cert even if anonymous
   // cipher was negotiated.  TLSv1 will reject such a request.  Hack: once a cert is
   // configured, allow only authenticated ciphers.
-  if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_AUTHENTICATE )) {
-      _log_ssl_error(ssl, "Failed to set cipher list to %s\n", CIPHERS_AUTHENTICATE);
+  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_AUTHENTICATE )) {
+      _log_ssl_error( "Failed to set cipher list to %s\n", CIPHERS_AUTHENTICATE);
       return -6;
   }
 
-  _log( ssl, "Configured local certificate file %s\n", certificate_file );
   return 0;
 }
 
 
-int pn_ssl_set_trusted_ca_db(pn_ssl_t *ssl,
-                             const char *certificate_db)
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
+                                    const char *certificate_db)
 {
-  if (!ssl) return 0;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set trusted CA db after SSL connection initialized.\n");
-    return -1;
-  }
+  if (!domain) return -1;
 
   // certificates can be either a file or a directory, which determines how it is passed
   // to SSL_CTX_load_verify_locations()
@@ -334,87 +562,66 @@ int pn_ssl_set_trusted_ca_db(pn_ssl_t *s
     file = certificate_db;
   }
 
-  if (SSL_CTX_load_verify_locations( ssl->ctx, file, dir ) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
+  if (SSL_CTX_load_verify_locations( domain->ctx, file, dir ) != 1) {
+    _log_ssl_error( "SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
     return -1;
   }
 
-  ssl->has_ca_db = true;
-
-  _log( ssl, "loaded trusted CA database: file=%s dir=%s\n", file, dir );
-  return 0;
-}
-
+  domain->has_ca_db = true;
 
-int pn_ssl_allow_unsecured_client(pn_ssl_t *ssl)
-{
-  if (ssl) {
-    if (ssl->mode != PN_SSL_MODE_SERVER) {
-      _log_error("Cannot permit unsecured clients - not a server.\n");
-      return -1;
-    }
-    ssl->allow_unsecured = true;
-    ssl->process_input = process_input_unknown;
-    ssl->process_output = process_output_unknown;
-    _log( ssl, "Allowing connections from unsecured clients.\n" );
-  }
   return 0;
 }
 
 
-int pn_ssl_set_peer_authentication(pn_ssl_t *ssl,
-                                   const pn_ssl_verify_mode_t mode,
-                                   const char *trusted_CAs)
+int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t *domain,
+                                          const pn_ssl_verify_mode_t mode,
+                                          const char *trusted_CAs)
 {
-  if (!ssl) return 0;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set peer authentication after SSL connection initialized.\n");
-    return -1;
-  }
+  if (!domain) return -1;
 
   switch (mode) {
   case PN_SSL_VERIFY_PEER:
+  case PN_SSL_VERIFY_PEER_NAME:
 
-    if (!ssl->has_ca_db) {
+    if (!domain->has_ca_db) {
       _log_error("Error: cannot verify peer without a trusted CA configured.\n"
-                 "       Use pn_ssl_set_trusted_ca_db()\n");
+                 "       Use pn_ssl_domain_set_trusted_ca_db()\n");
       return -1;
     }
 
-    if (ssl->mode == PN_SSL_MODE_SERVER) {
+    if (domain->mode == PN_SSL_MODE_SERVER) {
       // openssl requires that server connections supply a list of trusted CAs which is
       // sent to the client
       if (!trusted_CAs) {
         _log_error("Error: a list of trusted CAs must be provided.\n");
         return -1;
       }
-      if (!ssl->has_certificate) {
+      if (!domain->has_certificate) {
       _log_error("Error: Server cannot verify peer without configuring a certificate.\n"
-                 "       Use pn_ssl_set_credentials()\n");
+                 "       Use pn_ssl_domain_set_credentials()\n");
       }
 
-      ssl->trusted_CAs = pn_strdup( trusted_CAs );
+      if (domain->trusted_CAs) free(domain->trusted_CAs);
+      domain->trusted_CAs = pn_strdup( trusted_CAs );
       STACK_OF(X509_NAME) *cert_names;
-      cert_names = SSL_load_client_CA_file( ssl->trusted_CAs );
+      cert_names = SSL_load_client_CA_file( domain->trusted_CAs );
       if (cert_names != NULL)
-        SSL_CTX_set_client_CA_list(ssl->ctx, cert_names);
+        SSL_CTX_set_client_CA_list(domain->ctx, cert_names);
       else {
-        _log_error("Unable to process file of trusted CAs: %s\n", trusted_CAs);
+        _log_error("Error: Unable to process file of trusted CAs: %s\n", trusted_CAs);
         return -1;
       }
     }
 
-    SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
-    // verify_callback /*?verify callback?*/ );
+    SSL_CTX_set_verify( domain->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                        verify_callback);
 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
-    SSL_CTX_set_verify_depth(ssl->ctx, 1);
+    SSL_CTX_set_verify_depth(domain->ctx, 1);
 #endif
-    _log( ssl, "Peer authentication mode set to VERIFY-PEER\n");
     break;
 
   case PN_SSL_ANONYMOUS_PEER:   // hippie free love mode... :)
-    SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
-    _log( ssl, "Peer authentication mode set to ANONYMOUS-PEER\n");
+    SSL_CTX_set_verify( domain->ctx, SSL_VERIFY_NONE, NULL );
     break;
 
   default:
@@ -422,33 +629,44 @@ int pn_ssl_set_peer_authentication(pn_ss
     return -1;
   }
 
-  ssl->verify_mode = mode;
+  domain->verify_mode = mode;
   return 0;
 }
 
 
-int pn_ssl_get_peer_authentication(pn_ssl_t *ssl,
-                                   pn_ssl_verify_mode_t *mode,
-                                   char *trusted_CAs, size_t *trusted_CAs_size)
+int pn_ssl_init( pn_ssl_t *ssl, pn_ssl_domain_t *domain, const char *session_id)
 {
-  if (!ssl) return -1;
+  if (!ssl || !domain || ssl->domain) return -1;
 
-  if (mode) *mode = ssl->verify_mode;
-  if (trusted_CAs && trusted_CAs_size && *trusted_CAs_size) {
-    if (ssl->trusted_CAs) {
-      strncpy( trusted_CAs, ssl->trusted_CAs, *trusted_CAs_size );
-      trusted_CAs[*trusted_CAs_size - 1] = '\0';
-      *trusted_CAs_size = strlen(ssl->trusted_CAs) + 1;
-    } else {
-      *trusted_CAs = '\0';
-      *trusted_CAs_size = 0;
-    }
-  } else if (trusted_CAs_size) {
-    *trusted_CAs_size = (ssl->trusted_CAs) ? strlen(ssl->trusted_CAs) + 1 : 0;
+  ssl->domain = domain;
+  domain->ref_count++;
+  if (domain->allow_unsecured) {
+    ssl->process_input = process_input_unknown;
+    ssl->process_output = process_output_unknown;
+  } else {
+    ssl->process_input = process_input_ssl;
+    ssl->process_output = process_output_ssl;
   }
+
+  if (session_id && domain->mode == PN_SSL_MODE_CLIENT)
+    ssl->session_id = pn_strdup(session_id);
+
+  return init_ssl_socket(ssl);
+}
+
+
+int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain)
+{
+  if (!domain) return -1;
+  if (domain->mode != PN_SSL_MODE_SERVER) {
+    _log_error("Cannot permit unsecured clients - not a server.\n");
+    return -1;
+  }
+  domain->allow_unsecured = true;
   return 0;
 }
 
+
 bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size )
 {
   const SSL_CIPHER *c;
@@ -480,111 +698,14 @@ bool pn_ssl_get_protocol_name(pn_ssl_t *
 }
 
 
-
-int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mode_t mode)
-{
-  if (!ssl) return -1;
-  if (ssl->mode == mode) return 0;      // already set
-  if (ssl->ssl) {
-    _log_error("Unable to change mode once SSL is active.\n");
-    return -1;
-  }
-
-  // if changing the mode from the default, must release old context
-  if (ssl->ctx) SSL_CTX_free( ssl->ctx );
-
-  switch (mode) {
-  case PN_SSL_MODE_CLIENT:
-    _log( ssl, "Setting up Client SSL object.\n" );
-    ssl->mode = PN_SSL_MODE_CLIENT;
-    ssl->ctx = SSL_CTX_new(SSLv23_client_method());
-    if (!ssl->ctx) {
-      _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
-      return -1;
-    }
-    break;
-
-  case PN_SSL_MODE_SERVER:
-    _log( ssl, "Setting up Server SSL object.\n" );
-    ssl->mode = PN_SSL_MODE_SERVER;
-    ssl->ctx = SSL_CTX_new(SSLv23_server_method());
-    if (!ssl->ctx) {
-      _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
-      return -1;
-    }
-    break;
-
-  default:
-    _log_error("Invalid valid for pn_ssl_mode_t: %d\n", mode);
-    return -1;
-  }
-
-  // by default, allow anonymous ciphers so certificates are not required 'out of the box'
-  if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_ANONYMOUS )) {
-    _log_ssl_error(ssl, "Failed to set cipher list to %s\n", CIPHERS_ANONYMOUS);
-    return -2;
-  }
-
-  // ditto: by default do not authenticate the peer (can be done by SASL).
-  if (pn_ssl_set_peer_authentication( ssl, PN_SSL_ANONYMOUS_PEER, NULL )) {
-    return -2;
-  }
-
-  DH *dh = get_dh2048();
-  if (dh) {
-    SSL_CTX_set_tmp_dh(ssl->ctx, dh);
-    DH_free(dh);
-    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
-  }
-
-  return 0;
-}
-
-pn_ssl_t *pn_ssl(pn_transport_t *transport)
-{
-  if (!transport) return NULL;
-  if (transport->ssl) return transport->ssl;
-
-  if (!ssl_initialized) {
-    ssl_initialized = 1;
-    SSL_library_init();
-    SSL_load_error_strings();
-    OpenSSL_add_all_algorithms();
-  }
-
-  pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
-  if (!ssl) return NULL;
-
-  ssl->transport = transport;
-  ssl->process_input = process_input_ssl;
-  ssl->process_output = process_output_ssl;
-  transport->ssl = ssl;
-
-  ssl->trace = PN_TRACE_OFF;
-
-  // default mode is client
-  if (pn_ssl_init(ssl, PN_SSL_MODE_CLIENT)) {
-    free(ssl);
-    return NULL;
-  }
-  return ssl;
-}
-
-
 void pn_ssl_free( pn_ssl_t *ssl)
 {
   if (!ssl) return;
   _log( ssl, "SSL socket freed.\n" );
-  if (ssl->bio_ssl) BIO_free(ssl->bio_ssl);
-  if (ssl->ssl) SSL_free(ssl->ssl);
-  else {
-    if (ssl->bio_ssl_io) BIO_free(ssl->bio_ssl_io);
-    if (ssl->bio_net_io) BIO_free(ssl->bio_net_io);
-  }
-  if (ssl->ctx) SSL_CTX_free(ssl->ctx);
-
-  if (ssl->keyfile_pw) free(ssl->keyfile_pw);
-  if (ssl->trusted_CAs) free(ssl->trusted_CAs);
+  release_ssl_socket( ssl );
+  if (ssl->domain) pn_ssl_domain_free(ssl->domain);
+  if (ssl->session_id) free((void *)ssl->session_id);
+  if (ssl->peer_hostname) free((void *)ssl->peer_hostname);
 
   free(ssl);
 }
@@ -602,6 +723,22 @@ ssize_t pn_ssl_output(pn_ssl_t *ssl, cha
 }
 
 
+pn_ssl_t *pn_ssl(pn_transport_t *transport)
+{
+  if (!transport) return NULL;
+  if (transport->ssl) return transport->ssl;
+
+  pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
+  if (!ssl) return NULL;
+  ssl->transport = transport;
+  transport->ssl = ssl;
+
+  ssl->trace = (transport->disp) ? transport->disp->trace : PN_TRACE_OFF;
+
+  return ssl;
+}
+
+
 /** Private: */
 
 static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata)
@@ -616,6 +753,20 @@ static int start_ssl_shutdown( pn_ssl_t 
 {
   if (!ssl->ssl_shutdown) {
     _log(ssl, "Shutting down SSL connection...\n");
+    if (ssl->session_id) {
+      // save the negotiated credentials before we close the connection
+      pn_ssl_session_t *ssn = (pn_ssl_session_t *)calloc( 1, sizeof(pn_ssl_session_t));
+      if (ssn) {
+        ssn->id = pn_strdup( ssl->session_id );
+        ssn->session = SSL_get1_session( ssl->ssl );
+        if (ssn->session) {
+          _log( ssl, "Saving SSL session as %s\n", ssl->session_id );
+          LL_ADD( ssl->domain, ssn_cache, ssn );
+        } else {
+          ssl_session_free( ssn );
+        }
+      }
+    }
     ssl->ssl_shutdown = true;
     BIO_ssl_shutdown( ssl->bio_ssl );
   }
@@ -872,13 +1023,37 @@ static ssize_t process_output_ssl( pn_tr
 static int init_ssl_socket( pn_ssl_t *ssl )
 {
   if (ssl->ssl) return 0;
+  if (!ssl->domain) return -1;
 
-  ssl->ssl = SSL_new(ssl->ctx);
+  ssl->ssl = SSL_new(ssl->domain->ctx);
   if (!ssl->ssl) {
     _log_error( "SSL socket setup failure.\n" );
     return -1;
   }
 
+  // store backpointer to pn_ssl_t in SSL object:
+  SSL_set_ex_data(ssl->ssl, ssl_ex_data_index, ssl);
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+  if (ssl->peer_hostname && ssl->domain->mode == PN_SSL_MODE_CLIENT) {
+    SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname);
+  }
+#endif
+
+  // restore session, if available
+  if (ssl->session_id) {
+    pn_ssl_session_t *ssn = ssn_cache_find( ssl->domain, ssl->session_id );
+    if (ssn) {
+      _log( ssl, "Restoring previous session id=%s\n", ssn->id );
+      int rc = SSL_set_session( ssl->ssl, ssn->session );
+      if (rc != 1) {
+        _log( ssl, "Session restore failed, id=%s\n", ssn->id );
+      }
+      LL_REMOVE( ssl->domain, ssn_cache, ssn );
+      ssl_session_free( ssn );
+    }
+  }
+
   // now layer a BIO over the SSL socket
   ssl->bio_ssl = BIO_new(BIO_f_ssl());
   if (!ssl->bio_ssl) {
@@ -894,7 +1069,7 @@ static int init_ssl_socket( pn_ssl_t *ss
   }
   SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io);
 
-  if (ssl->mode == PN_SSL_MODE_SERVER) {
+  if (ssl->domain->mode == PN_SSL_MODE_SERVER) {
     SSL_set_accept_state(ssl->ssl);
     BIO_set_ssl_mode(ssl->bio_ssl, 0);  // server mode
     _log( ssl, "Server SSL socket created.\n" );
@@ -906,6 +1081,21 @@ static int init_ssl_socket( pn_ssl_t *ss
   return 0;
 }
 
+static void release_ssl_socket( pn_ssl_t *ssl )
+{
+  if (ssl->bio_ssl) BIO_free(ssl->bio_ssl);
+  if (ssl->ssl) SSL_free(ssl->ssl);
+  else {
+    if (ssl->bio_ssl_io) BIO_free(ssl->bio_ssl_io);
+    if (ssl->bio_net_io) BIO_free(ssl->bio_net_io);
+  }
+  ssl->bio_ssl = NULL;
+  ssl->bio_ssl_io = NULL;
+  ssl->bio_net_io = NULL;
+  ssl->ssl = NULL;
+}
+
+
 //////// CLEARTEXT CONNECTIONS
 
 static ssize_t process_input_cleartext(pn_transport_t *transport, const char *input_data, size_t len)
@@ -1010,3 +1200,51 @@ void pn_ssl_trace(pn_ssl_t *ssl, pn_trac
 {
   ssl->trace = trace;
 }
+
+
+pn_ssl_resume_status_t pn_ssl_resume_status( pn_ssl_t *ssl )
+{
+  if (!ssl || !ssl->ssl) return PN_SSL_RESUME_UNKNOWN;
+  switch (SSL_session_reused( ssl->ssl )) {
+  case 0: return PN_SSL_RESUME_NEW;
+  case 1: return PN_SSL_RESUME_REUSED;
+  default: break;
+  }
+  return PN_SSL_RESUME_UNKNOWN;
+}
+
+
+int pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname )
+{
+  if (!ssl) return -1;
+
+  if (ssl->peer_hostname) free((void *)ssl->peer_hostname);
+  ssl->peer_hostname = NULL;
+  if (hostname) {
+    ssl->peer_hostname = pn_strdup(hostname);
+    if (!ssl->peer_hostname) return -2;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+    if (ssl->ssl && ssl->domain && ssl->domain->mode == PN_SSL_MODE_CLIENT) {
+      SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname);
+    }
+#endif
+  }
+  return 0;
+}
+
+int pn_ssl_get_peer_hostname( pn_ssl_t *ssl, char *hostname, size_t *bufsize )
+{
+  if (!ssl) return -1;
+  if (!ssl->peer_hostname) {
+    *bufsize = 0;
+    if (hostname) *hostname = '\0';
+    return 0;
+  }
+  int len = strlen(ssl->peer_hostname);
+  if (hostname) {
+    if (len >= *bufsize) return -1;
+    strcpy( hostname, ssl->peer_hostname );
+  }
+  *bufsize = len;
+  return 0;
+}

Modified: qpid/proton/branches/jni-binding/proton-c/src/ssl/ssl_stub.c
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-c/src/ssl/ssl_stub.c?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-c/src/ssl/ssl_stub.c (original)
+++ qpid/proton/branches/jni-binding/proton-c/src/ssl/ssl_stub.c Tue Jan  8 08:29:31 2013
@@ -35,72 +35,86 @@ pn_ssl_t *pn_ssl(pn_transport_t *transpo
   return NULL;
 }
 
-int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mode_t mode)
+int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_domain_t *domain,
+                const char *session_id)
 {
   return -1;
 }
 
+void pn_ssl_free( pn_ssl_t *ssl)
+{
+}
 
-int pn_ssl_set_credentials(pn_ssl_t *ssl,
-                           const char *certificate_file,
-                           const char *private_key_file,
-                           const char *password)
+void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace)
 {
-  return -1;
 }
 
-int pn_ssl_set_trusted_ca_db(pn_ssl_t *ssl,
-                             const char *certificate_db)
+ssize_t pn_ssl_input(pn_ssl_t *ssl, const char *bytes, size_t available)
 {
-  return -1;
+  return PN_EOS;
 }
 
-int pn_ssl_allow_unsecured_client(pn_ssl_t *ssl)
+ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size)
 {
-  return -1;
+  return PN_EOS;
 }
 
+bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size)
+{
+  return false;
+}
 
-int pn_ssl_set_peer_authentication(pn_ssl_t *ssl,
-                                   const pn_ssl_verify_mode_t mode,
-                                   const char *trusted_CAs)
+bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size)
 {
-  return -1;
+  return false;
+}
+
+pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode)
+{
+  return NULL;
 }
 
+void pn_ssl_domain_free( pn_ssl_domain_t *d )
+{
+}
 
-int pn_ssl_get_peer_authentication(pn_ssl_t *ssl,
-                                   pn_ssl_verify_mode_t *mode,
-                                   char *trusted_CAs, size_t *trusted_CAs_size)
+int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
+                               const char *certificate_file,
+                               const char *private_key_file,
+                               const char *password)
 {
   return -1;
 }
 
-void pn_ssl_free( pn_ssl_t *ssl)
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
+                                const char *certificate_db)
 {
+  return -1;
 }
 
-void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace)
+int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t *domain,
+                                   const pn_ssl_verify_mode_t mode,
+                                   const char *trusted_CAs)
 {
+  return -1;
 }
 
-ssize_t pn_ssl_input(pn_ssl_t *ssl, const char *bytes, size_t available)
+int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain)
 {
-  return PN_EOS;
+  return -1;
 }
 
-ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size)
+pn_ssl_resume_status_t pn_ssl_resume_status( pn_ssl_t *s )
 {
-  return PN_EOS;
+  return PN_SSL_RESUME_UNKNOWN;
 }
 
-bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size)
+int pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname)
 {
-  return false;
+  return -1;
 }
 
-bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size)
+int pn_ssl_get_peer_hostname( pn_ssl_t *ssl, char *hostname, size_t *bufsize )
 {
-  return false;
+  return -1;
 }
-

Modified: qpid/proton/branches/jni-binding/proton-c/src/util.c
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-c/src/util.c?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-c/src/util.c (original)
+++ qpid/proton/branches/jni-binding/proton-c/src/util.c Tue Jan  8 08:29:31 2013
@@ -132,11 +132,20 @@ void pn_fatal(char *fmt, ...)
   va_end(ap);
 }
 
+static bool pn_i_eq_nocase(const char *a, const char *b)
+{
+    while (*b) {
+        if (tolower(*a++) != tolower(*b++))
+            return false;
+    }
+    return !(*a);
+}
+
 bool pn_env_bool(const char *name)
 {
   char *v = getenv(name);
-  return v && (!strcasecmp(v, "true") || !strcasecmp(v, "1") ||
-               !strcasecmp(v, "yes"));
+  return v && (pn_i_eq_nocase(v, "true") || pn_i_eq_nocase(v, "1") ||
+               pn_i_eq_nocase(v, "yes") || pn_i_eq_nocase(v, "on"));
 }
 
 char *pn_strdup(const char *src)

Modified: qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/AmqpSender.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/AmqpSender.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/AmqpSender.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/AmqpSender.java Tue Jan  8 08:29:31 2013
@@ -76,7 +76,7 @@ public class AmqpSender extends AmqpLink
     final LinkedList<MessageDelivery> outbound = new LinkedList<MessageDelivery>();
     long outboundBufferSize;
 
-    public MessageDelivery send(MessageImpl message) {
+    public MessageDelivery send(Message message) {
         assertExecuting();
         MessageDelivery rc = new MessageDelivery(message) {
             @Override

Modified: qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/MessageDelivery.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/MessageDelivery.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/MessageDelivery.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/contrib/proton-hawtdispatch/src/main/java/org/apache/qpid/proton/hawtdispatch/api/MessageDelivery.java Tue Jan  8 08:29:31 2013
@@ -17,44 +17,38 @@
 
 package org.apache.qpid.proton.hawtdispatch.api;
 
-import org.apache.qpid.proton.hawtdispatch.impl.DroppingWritableBuffer;
+import org.apache.qpid.proton.amqp.transport.DeliveryState;
+import org.apache.qpid.proton.engine.impl.DeliveryImpl;
 import org.apache.qpid.proton.hawtdispatch.impl.Watch;
 import org.apache.qpid.proton.hawtdispatch.impl.WatchBase;
-import org.apache.qpid.proton.codec.CompositeWritableBuffer;
-import org.apache.qpid.proton.codec.WritableBuffer;
-import org.apache.qpid.proton.engine.impl.DeliveryImpl;
 import org.apache.qpid.proton.message.Message;
-import org.apache.qpid.proton.amqp.transport.DeliveryState;
 import org.apache.qpid.proton.message.impl.MessageImpl;
 import org.fusesource.hawtbuf.Buffer;
 import org.fusesource.hawtdispatch.Task;
 
-import java.nio.ByteBuffer;
-
 /**
  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  */
 public abstract class MessageDelivery extends WatchBase {
 
     final int initialSize;
-    private MessageImpl message;
+    private Message message;
     private Buffer encoded;
     public DeliveryImpl delivery;
     private int sizeHint = 1024*4;
 
-    static Buffer encode(MessageImpl message, int sizeHint) {
-        ByteBuffer buffer = ByteBuffer.wrap(new byte[sizeHint]);
-        DroppingWritableBuffer overflow = new DroppingWritableBuffer();
-        int c = message.encode(new CompositeWritableBuffer(new WritableBuffer.ByteBufferWrapper(buffer), overflow));
-        if( overflow.position() > 0 ) {
-            buffer = ByteBuffer.wrap(new byte[sizeHint+overflow.position()]);
-            c = message.encode(new WritableBuffer.ByteBufferWrapper(buffer));
+    static Buffer encode(Message message, int sizeHint) {
+        byte[] buffer = new byte[sizeHint];
+        int size = ((MessageImpl)message).encode2(buffer, 0, sizeHint);
+        if( size > sizeHint ) {
+            buffer = new byte[size];
+            size = message.encode(buffer, 0, size);
         }
-        return new Buffer(buffer.array(), 0, c);
+        return new Buffer(buffer, 0, size);
     }
 
-    static MessageImpl decode(Buffer buffer) {
-        MessageImpl msg = new MessageImpl();
+    static Message decode(Buffer buffer) {
+        Message msg = new MessageImpl();
         int offset = buffer.offset;
         int len = buffer.length;
         while( len > 0 ) {
@@ -66,7 +60,7 @@ public abstract class MessageDelivery ex
         return msg;
     }
 
-    public MessageDelivery(MessageImpl message) {
+    public MessageDelivery(Message message) {
         this(message, encode(message, 1024*4));
     }
 
@@ -74,7 +68,7 @@ public abstract class MessageDelivery ex
         this(null, encoded);
     }
 
-    public MessageDelivery(MessageImpl message, Buffer encoded) {
+    public MessageDelivery(Message message, Buffer encoded) {
         this.message = message;
         this.encoded = encoded;
         sizeHint = this.encoded.length;

Modified: qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/AMQPNativeOutboundTransformer.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/AMQPNativeOutboundTransformer.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/AMQPNativeOutboundTransformer.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/AMQPNativeOutboundTransformer.java Tue Jan  8 08:29:31 2013
@@ -17,6 +17,7 @@
 package org.apache.qpid.proton.jms;
 
 import org.apache.qpid.proton.codec.CompositeWritableBuffer;
+import org.apache.qpid.proton.codec.DroppingWritableBuffer;
 import org.apache.qpid.proton.codec.WritableBuffer;
 import org.apache.qpid.proton.amqp.UnsignedInteger;
 

Modified: qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/JMSMappingOutboundTransformer.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/JMSMappingOutboundTransformer.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/JMSMappingOutboundTransformer.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/contrib/proton-jms/src/main/java/org/apache/qpid/proton/jms/JMSMappingOutboundTransformer.java Tue Jan  8 08:29:31 2013
@@ -19,6 +19,7 @@ package org.apache.qpid.proton.jms;
 import org.apache.qpid.proton.amqp.messaging.Section;
 import org.apache.qpid.proton.codec.CompositeWritableBuffer;
 import org.apache.qpid.proton.codec.WritableBuffer;
+import org.apache.qpid.proton.codec.DroppingWritableBuffer;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.UnsignedByte;

Modified: qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Ssl.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Ssl.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Ssl.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Ssl.java Tue Jan  8 08:29:31 2013
@@ -1,6 +1,4 @@
-package org.apache.qpid.proton.engine;
 /*
- *
  * 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
@@ -19,130 +17,31 @@ package org.apache.qpid.proton.engine;
  * under the License.
  *
  */
+package org.apache.qpid.proton.engine;
 
+/**
+ * I represent the details of a particular SSL session.
+ */
 public interface Ssl
 {
-    public enum Mode
-    {
-        CLIENT,    /** Local connection endpoint is an SSL client */
-        SERVER     /** Local connection endpoint is an SSL server */
-    }
-
-
-    /** Initialize the pn_ssl_t object.
-     *
-     * An SSL object be either an SSL server or an SSL client.  It cannot be both. Those
-     * transports that will be used to accept incoming connection requests must be configured
-     * as an SSL server. Those transports that will be used to initiate outbound connections
-     * must be configured as an SSL client.
-     *
-     */
-    void init(Mode mode);
-
-    Mode getMode();
-
-    /** Set the certificate that identifies the local node to the remote.
-     *
-     * This certificate establishes the identity for the local node.  It will be sent to the
-     * remote if the remote needs to verify the identity of this node.  This may be used for
-     * both SSL servers and SSL clients (if client authentication is required by the server).
-     *
-     * @param certificate_file path to file/database containing the identifying
-     * certificate.
-     * @param private_key_file path to file/database containing the private key used to
-     * sign the certificate
-     * @param password the password used to sign the key, else NULL if key is not
-     * protected.
-     */
-     void setCredentials( String certificate_file,
-                         String private_key_file,
-                         String password);
-
-     String getPrivateKeyFile(); // TODO
-     String getPrivateKeyPassword();
-
-     String getCertificateFile();
-
-    /** Configure the set of trusted CA certificates used by this node to verify peers.
-     *
-     * If the local SSL client/server needs to verify the identity of the remote, it must
-     * validate the signature of the remote's certificate.  This function sets the database of
-     * trusted CAs that will be used to verify the signature of the remote's certificate.
-     *
-     * @param certificate_db database of trusted CAs, used to authenticate the peer.
-     */
-
-    void setTrustedCaDb(String certificate_db);
-
-    String getTrustedCaDb();
-
-    /** Permit a server to accept connection requests from non-SSL clients.
-     *
-     * This configures the server to "sniff" the incoming client data stream, and dynamically
-     * determine whether SSL/TLS is being used.  This option is disabled by default: only
-     * clients using SSL/TLS are accepted.
-     *
-     */
-    void allowUnsecuredClient(boolean allowUnsecured);
-
-    /** Determines the level of peer validation.
-     *
-     *  VERIFY_PEER will only connect to those peers that provide a valid identifying
-     *  certificate signed by a trusted CA and are using an authenticated cipher.
-     *  ANONYMOUS_PEER does not require a valid certificate, and permits use of ciphers that
-     *  do not provide authentication.
-     *
-     *  ANONYMOUS_PEER is configured by default.
-     *
-     *  These settings can be changed via ::pn_ssl_set_peer_authentication()
-     */
-    public enum VerifyMode
-    {
-      VERIFY_PEER,     /** require peer to provide a valid identifying certificate */
-      ANONYMOUS_PEER,  /** do not require a certificate nor cipher authorization */
-    }
-
-
-    /** Configure the level of verification used on the peer certificate.
-     *
-     * This method controls how the peer's certificate is validated, if at all.  By default,
-     * neither servers nor clients attempt to verify their peers (PN_SSL_ANONYMOUS_PEER).
-     * Once certificates and trusted CAs are configured, peer verification can be enabled.
-     *
-     * In order to verify a peer, a trusted CA must be configured. See
-     * #setTrustedCaDb().
-     *
-     * @note Servers must provide their own certificate when verifying a peer.  See
-     * #setCredentials().
-     *
-     * @param mode the level of validation to apply to the peer
-     */
-    void setPeerAuthentication(VerifyMode mode);
-
-    VerifyMode getPeerAuthentication();
-
-    /** Get the name of the Cipher that is currently in use.
+    /**
+     * Get the name of the Cipher that is currently in use.
      *
      * Gets a text description of the cipher that is currently active, or returns null if SSL
-     * is not active (no cipher).  Note that the cipher in use may change over time due to
+     * is not active (no cipher). Note that the cipher in use may change over time due to
      * renegotiation or other changes to the SSL state.
      *
      * @return the name of the cipher in use, or null if none
      */
     String getCipherName();
 
-    /** Get the name of the SSL protocol that is currently in use.
+    /**
+     * Get the name of the SSL protocol that is currently in use.
      *
      * Gets a text description of the SSL protocol that is currently active, or null if SSL
-     * is not active.  Note that the protocol may change over time due to renegotiation.
+     * is not active. Note that the protocol may change over time due to renegotiation.
      *
      * @return the name of the protocol in use, or null if none
      */
     String getProtocolName();
-
-
-
-
-
-
 }

Modified: qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Transport.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Transport.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Transport.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/engine/Transport.java Tue Jan  8 08:29:31 2013
@@ -59,6 +59,20 @@ public interface Transport extends Endpo
 
     Sasl sasl();
 
-    Ssl ssl();
+    /**
+     * Wrap this transport's output and input to apply SSL encryption and decryption respectively.
+     *
+     * This method is expected to be called at most once. A subsequent invocation will return the same
+     * {@link Ssl} object, regardless of the parameters supplied.
+     *
+     * @param sslDomain the SSL settings to use
+     * @param sslPeerDetails may be null, in which case SSL session resume will not be attempted
+     * @return an {@link Ssl} object representing the SSL session.
+     */
+    Ssl ssl(SslDomain sslDomain, SslPeerDetails sslPeerDetails);
 
+    /**
+     * As per {@link #ssl(SslDomain, SslPeerDetails)} but no attempt is made to resume a previous SSL session.
+     */
+    Ssl ssl(SslDomain sslDomain);
 }

Modified: qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/messenger/Messenger.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/messenger/Messenger.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/messenger/Messenger.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton-api/src/main/java/org/apache/qpid/proton/messenger/Messenger.java Tue Jan  8 08:29:31 2013
@@ -145,9 +145,6 @@ public interface Messenger
      */
     int incoming();
 
-    AcceptMode getAcceptMode();
-    void setAcceptMode(AcceptMode mode);
-
     int getIncomingWindow();
     void setIncomingWindow(int window);
 

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/TransportImpl.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/TransportImpl.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/TransportImpl.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/TransportImpl.java Tue Jan  8 08:29:31 2013
@@ -34,8 +34,11 @@ import org.apache.qpid.proton.engine.End
 import org.apache.qpid.proton.engine.EndpointState;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Ssl;
+import org.apache.qpid.proton.engine.SslDomain;
+import org.apache.qpid.proton.engine.SslPeerDetails;
 import org.apache.qpid.proton.engine.Transport;
 import org.apache.qpid.proton.engine.TransportException;
+import org.apache.qpid.proton.engine.impl.ssl.ProtonSslEngineProvider;
 import org.apache.qpid.proton.engine.impl.ssl.SslImpl;
 import org.apache.qpid.proton.framing.TransportFrame;
 import org.apache.qpid.proton.amqp.transport.Attach;
@@ -238,12 +241,19 @@ public class TransportImpl extends Endpo
 
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Note that sslDomain must implement {@link ProtonSslEngineProvider}. This is not possible
+     * enforce at the API level because {@link ProtonSslEngineProvider} is not part of the
+     * public Proton API.</p>
+     */
     @Override
-    public Ssl ssl()
+    public Ssl ssl(SslDomain sslDomain, SslPeerDetails sslPeerDetails)
     {
         if (_ssl == null)
         {
-            _ssl = new SslImpl();
+            _ssl = new SslImpl(sslDomain, sslPeerDetails);
             TransportWrapper transportWrapper = _ssl.wrap(_inputProcessor, _outputProcessor);
             _inputProcessor = transportWrapper;
             _outputProcessor = transportWrapper;
@@ -251,6 +261,12 @@ public class TransportImpl extends Endpo
         return _ssl;
     }
 
+    @Override
+    public Ssl ssl(SslDomain sslDomain)
+    {
+        return ssl(sslDomain, null);
+    }
+
     private void clearTransportWorkList()
     {
         DeliveryImpl delivery = _connectionEndpoint.getTransportWorkHead();

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/DefaultSslEngineFacade.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/DefaultSslEngineFacade.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/DefaultSslEngineFacade.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/DefaultSslEngineFacade.java Tue Jan  8 08:29:31 2013
@@ -26,7 +26,8 @@ import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 import javax.net.ssl.SSLException;
 
-class DefaultSslEngineFacade implements SslEngineFacade
+
+class DefaultSslEngineFacade implements ProtonSslEngine
 {
     private final SSLEngine _sslEngine;
 
@@ -82,4 +83,10 @@ class DefaultSslEngineFacade implements 
     {
         return _sslEngine.getHandshakeStatus();
     }
+
+    @Override
+    public boolean getUseClientMode()
+    {
+        return _sslEngine.getUseClientMode();
+    }
 }

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SimpleSslTransportWrapper.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SimpleSslTransportWrapper.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SimpleSslTransportWrapper.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SimpleSslTransportWrapper.java Tue Jan  8 08:29:31 2013
@@ -31,7 +31,6 @@ import javax.net.ssl.SSLEngineResult.Sta
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 
-import org.apache.qpid.proton.engine.Ssl;
 import org.apache.qpid.proton.engine.TransportException;
 import org.apache.qpid.proton.engine.impl.TransportInput;
 import org.apache.qpid.proton.engine.impl.TransportOutput;
@@ -39,7 +38,6 @@ import org.apache.qpid.proton.engine.imp
 /**
  * TODO close the SSLEngine when told to, and modify {@link #input(byte[], int, int)} and {@link #output(byte[], int, int)}
  * to respond appropriately thereafter.
- * TODO move SSL and possible byte management classes into separate package
  */
 public class SimpleSslTransportWrapper implements SslTransportWrapper
 {
@@ -55,9 +53,8 @@ public class SimpleSslTransportWrapper i
 
     private final TransportInput _underlyingInput;
     private final TransportOutput _underlyingOutput;
-    private final Ssl _sslParams;
 
-    private SslEngineFacade _sslEngine;
+    private ProtonSslEngine _sslEngine;
 
     /** Used by {@link #output(byte[], int, int)}. Acts as a buffer for the output from underlyingOutput */
     private ByteHolder _clearOutputHolder;
@@ -77,22 +74,11 @@ public class SimpleSslTransportWrapper i
     /** could change during the lifetime of the ssl connection owing to renegotiation. */
     private String _protocolName;
 
-    private final SslEngineFacadeFactory _sslEngineFacadeFactory;
-
-    public SimpleSslTransportWrapper(Ssl sslConfiguration, TransportInput underlyingInput, TransportOutput underlyingOutput)
-    {
-        this(sslConfiguration, underlyingInput, underlyingOutput, new SslEngineFacadeFactory());
-    }
-
-    SimpleSslTransportWrapper(Ssl sslConfiguration,
-            TransportInput underlyingInput, TransportOutput underlyingOutput,
-            SslEngineFacadeFactory sslEngineFacadeFactory)
+    SimpleSslTransportWrapper(ProtonSslEngine sslEngine, TransportInput underlyingInput, TransportOutput underlyingOutput)
     {
-        _sslParams = sslConfiguration;
         _underlyingInput = underlyingInput;
         _underlyingOutput = underlyingOutput;
-        _sslEngineFacadeFactory = sslEngineFacadeFactory;
-        _sslEngine = _sslEngineFacadeFactory.createSslEngineFacade(sslConfiguration);
+        _sslEngine = sslEngine;
         createByteHolders();
     }
 
@@ -134,10 +120,7 @@ public class SimpleSslTransportWrapper i
                 runDelegatedTasks(result);
                 updateCipherAndProtocolName(result);
 
-                if(_logger.isLoggable(Level.FINEST))
-                {
-                    _logger.log(Level.FINEST, _sslParams.getMode() + " input " + resultToString(result));
-                }
+                logEngineClientModeAndResult(result, "input");
 
                 Status sslResultStatus = result.getStatus();
                 HandshakeStatus handshakeStatus = result.getHandshakeStatus();
@@ -180,19 +163,10 @@ public class SimpleSslTransportWrapper i
         }
         catch(SSLException e)
         {
-            throw new TransportException(e);
+            throw new TransportException("Problem during input. useClientMode: " + _sslEngine.getUseClientMode(), e);
         }
     }
 
-    private String resultToString(SSLEngineResult result)
-    {
-        return new StringBuilder("[SSLEngineResult status = ").append(result.getStatus())
-                .append(" handshakeStatus = ").append(result.getHandshakeStatus())
-                .append(" bytesConsumed = ").append(result.bytesConsumed())
-                .append(" bytesProduced = ").append(result.bytesProduced())
-                .append("]").toString();
-    }
-
     /**
      * Write encoded output to the supplied destination.
      *
@@ -237,6 +211,8 @@ public class SimpleSslTransportWrapper i
                 }
 
                 SSLEngineResult result = _sslEngine.wrap(_clearOutputHolder.prepareToRead(), sslWrapDst);
+                logEngineClientModeAndResult(result, "output");
+
                 _clearOutputHolder.prepareToWrite();
 
                 Status sslResultStatus = result.getStatus();
@@ -270,7 +246,7 @@ public class SimpleSslTransportWrapper i
         }
         catch(SSLException e)
         {
-            throw new TransportException("Mode " + _sslParams.getMode(), e);
+            throw new TransportException("Problem during output. useClientMode: " + _sslEngine.getUseClientMode(), e);
         }
     }
 
@@ -315,9 +291,21 @@ public class SimpleSslTransportWrapper i
         }
     }
 
-    public static void main(String[] args)
+    private void logEngineClientModeAndResult(SSLEngineResult result, String direction)
     {
-        _logger.info("PHDEBUG in main");
-        _logger.info("PHDEBUG in main2");
+        if(_logger.isLoggable(Level.FINEST))
+        {
+            _logger.log(Level.FINEST, "useClientMode = " + _sslEngine.getUseClientMode() + " direction = " + direction
+                    + " " + resultToString(result));
+        }
+    }
+
+    private String resultToString(SSLEngineResult result)
+    {
+        return new StringBuilder("[SSLEngineResult status = ").append(result.getStatus())
+                .append(" handshakeStatus = ").append(result.getHandshakeStatus())
+                .append(" bytesConsumed = ").append(result.bytesConsumed())
+                .append(" bytesProduced = ").append(result.bytesProduced())
+                .append("]").toString();
     }
 }

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslEngineFacadeFactory.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslEngineFacadeFactory.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslEngineFacadeFactory.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslEngineFacadeFactory.java Tue Jan  8 08:29:31 2013
@@ -23,11 +23,12 @@ package org.apache.qpid.proton.engine.im
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
-import java.security.Key;
 import java.security.KeyManagementException;
+import java.security.KeyPair;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
 import java.security.Security;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
@@ -46,9 +47,8 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
-import org.apache.qpid.proton.engine.Ssl;
-import org.apache.qpid.proton.engine.Ssl.Mode;
-import org.apache.qpid.proton.engine.Ssl.VerifyMode;
+import org.apache.qpid.proton.engine.SslDomain;
+import org.apache.qpid.proton.engine.SslPeerDetails;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMException;
 import org.bouncycastle.openssl.PEMReader;
@@ -81,25 +81,51 @@ public class SslEngineFacadeFactory
             "SSL_DH_anon_WITH_DES_CBC_SHA",
             "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
 
-    public SslEngineFacade createSslEngineFacade(Ssl sslConfiguration)
+    /** lazily initialized */
+    private SSLContext _sslContext;
+
+
+    /**
+     * Returns a {@link ProtonSslEngine}. May cache the domain's settings so callers should invoke
+     * {@link #resetCache()} if the domain changes.
+     *
+     * @param peerDetails may be used to return an engine that supports SSL resume.
+     */
+    public ProtonSslEngine createProtonSslEngine(SslDomain domain, SslPeerDetails peerDetails)
     {
-        SSLEngine engine = createSslEngine(sslConfiguration);
+        SSLEngine engine = createAndInitialiseSslEngine(domain, peerDetails);
+        if(_logger.isLoggable(Level.FINE))
+        {
+            _logger.fine("Created SSL engine: " + engineToString(engine));
+        }
         return new DefaultSslEngineFacade(engine);
     }
 
-    private SSLEngine createSslEngine(Ssl sslConfiguration)
+
+    /**
+     * Guarantees that no cached settings are used in subsequent calls to
+     * {@link #createProtonSslEngine(SslDomain, SslPeerDetails)}.
+     */
+    public void resetCache()
     {
-        SSLEngine sslEngine;
+        _sslContext = null;
+    }
+
 
-        SSLContext sslContext = createSslContext(sslConfiguration);
-        sslEngine = sslContext.createSSLEngine();
-        if (sslConfiguration.getPeerAuthentication() == VerifyMode.ANONYMOUS_PEER)
+    private SSLEngine createAndInitialiseSslEngine(SslDomain domain, SslPeerDetails peerDetails)
+    {
+        SslDomain.Mode mode = domain.getMode();
+
+        SSLContext sslContext = getOrCreateSslContext(domain);
+        SSLEngine sslEngine = createSslEngine(sslContext, peerDetails);
+
+        if (domain.getPeerAuthentication() == SslDomain.VerifyMode.ANONYMOUS_PEER)
         {
             addAnonymousCipherSuites(sslEngine);
         }
         else
         {
-            if (sslConfiguration.getMode() == Mode.SERVER)
+            if (mode == SslDomain.Mode.SERVER)
             {
                 sslEngine.setNeedClientAuth(true);
             }
@@ -107,87 +133,129 @@ public class SslEngineFacadeFactory
 
         if(_logger.isLoggable(Level.FINE))
         {
-            _logger.log(Level.FINE, sslConfiguration.getMode() + " Enabled cipher suites " + Arrays.asList(sslEngine.getEnabledCipherSuites()));
+            _logger.log(Level.FINE, mode + " Enabled cipher suites " + Arrays.asList(sslEngine.getEnabledCipherSuites()));
         }
 
-        boolean useClientMode = sslConfiguration.getMode() == Mode.CLIENT ? true : false;
+        boolean useClientMode = mode == SslDomain.Mode.CLIENT ? true : false;
         sslEngine.setUseClientMode(useClientMode);
 
         return sslEngine;
     }
 
-    private SSLContext createSslContext(Ssl sslConfiguration)
+    /**
+     * @param sslPeerDetails is allowed to be null. A non-null value is used to hint that SSL resumption
+     * should be attempted
+     */
+    private SSLEngine createSslEngine(SSLContext sslContext, SslPeerDetails sslPeerDetails)
     {
-        final char[] dummyPassword = "unused-passphrase".toCharArray(); // Dummy password required by KeyStore and KeyManagerFactory, but never referred to again
+        final SSLEngine sslEngine;
+        if(sslPeerDetails == null)
+        {
+            sslEngine = sslContext.createSSLEngine();
+        }
+        else
+        {
+            sslEngine = sslContext.createSSLEngine(sslPeerDetails.getHostname(), sslPeerDetails.getPort());
+        }
+        return sslEngine;
+    }
 
-        try
+    private SSLContext getOrCreateSslContext(SslDomain sslDomain)
+    {
+        if(_sslContext == null)
         {
-            SSLContext sslContext = SSLContext.getInstance(TLS_PROTOCOL);
-            KeyStore ksKeys = createKeyStoreFrom(sslConfiguration, dummyPassword);
+            if(_logger.isLoggable(Level.FINE))
+            {
+                _logger.fine("lazily creating new SSLContext using domain " + sslDomain);
+            }
 
-            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-            kmf.init(ksKeys, dummyPassword);
+            final char[] dummyPassword = "unused-passphrase".toCharArray(); // Dummy password required by KeyStore and KeyManagerFactory, but never referred to again
 
-            final TrustManager[] trustManagers;
-            if (sslConfiguration.getTrustedCaDb() == null && sslConfiguration.getPeerAuthentication() == VerifyMode.ANONYMOUS_PEER)
+            try
             {
-                trustManagers = new TrustManager[] { new AlwaysTrustingTrustManager() };
+                SSLContext sslContext = SSLContext.getInstance(TLS_PROTOCOL);
+                KeyStore ksKeys = createKeyStoreFrom(sslDomain, dummyPassword);
+
+                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+                kmf.init(ksKeys, dummyPassword);
+
+                final TrustManager[] trustManagers;
+                if (sslDomain.getPeerAuthentication() == SslDomain.VerifyMode.ANONYMOUS_PEER)
+                {
+                    trustManagers = new TrustManager[] { new AlwaysTrustingTrustManager() };
+                }
+                else
+                {
+                    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+                    tmf.init(ksKeys);
+                    trustManagers = tmf.getTrustManagers();
+                }
+
+                sslContext.init(kmf.getKeyManagers(), trustManagers, null);
+                _sslContext = sslContext;
             }
-            else
+            catch (NoSuchAlgorithmException e)
             {
-                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-                tmf.init(ksKeys);
-                trustManagers = tmf.getTrustManagers();
+                throw new IllegalStateException("Unexpected exception creating SSLContext", e);
+            }
+            catch (KeyStoreException e)
+            {
+                throw new IllegalStateException("Unexpected exception creating SSLContext", e);
+            }
+            catch (UnrecoverableKeyException e)
+            {
+                throw new IllegalStateException("Unexpected exception creating SSLContext", e);
+            }
+            catch (KeyManagementException e)
+            {
+                throw new IllegalStateException("Unexpected exception creating SSLContext", e);
             }
-
-            sslContext.init(kmf.getKeyManagers(), trustManagers, null);
-            return sslContext;
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            throw new IllegalStateException("Unexpected exception creating SSLContext", e);
-        }
-        catch (KeyStoreException e)
-        {
-            throw new IllegalStateException("Unexpected exception creating SSLContext", e);
-        }
-        catch (UnrecoverableKeyException e)
-        {
-            throw new IllegalStateException("Unexpected exception creating SSLContext", e);
-        }
-        catch (KeyManagementException e)
-        {
-            throw new IllegalStateException("Unexpected exception creating SSLContext", e);
         }
+        return _sslContext;
     }
 
-    private KeyStore createKeyStoreFrom(Ssl sslConfiguration, char[] dummyPassword)
+    private KeyStore createKeyStoreFrom(SslDomain sslDomain, char[] dummyPassword)
     {
         try
         {
             KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
             keystore.load(null, null);
 
-            if (sslConfiguration.getTrustedCaDb() != null)
+            if (sslDomain.getTrustedCaDb() != null)
             {
                 String caCertAlias = "cacert";
 
                 if(_logger.isLoggable(Level.FINE))
                 {
-                    _logger.log(Level.FINE, "_sslParams.getTrustedCaDb() : " + sslConfiguration.getTrustedCaDb());
+                    _logger.log(Level.FINE, "_sslParams.getTrustedCaDb() : " + sslDomain.getTrustedCaDb());
                 }
-                Certificate trustedCaCert = (Certificate) readPemObject(sslConfiguration.getTrustedCaDb());
+                Certificate trustedCaCert = (Certificate) readPemObject(sslDomain.getTrustedCaDb(), null, Certificate.class);
                 keystore.setCertificateEntry(caCertAlias, trustedCaCert);
             }
 
-            if (sslConfiguration.getCertificateFile() != null
-                    && sslConfiguration.getPrivateKeyFile() != null)
+            if (sslDomain.getCertificateFile() != null
+                    && sslDomain.getPrivateKeyFile() != null)
             {
                 String clientPrivateKeyAlias = "clientPrivateKey";
-                Certificate clientCertificate = (Certificate) readPemObject(sslConfiguration.getCertificateFile());
-                Key clientPrivateKey = (Key) readPemObject(
-                        sslConfiguration.getPrivateKeyFile(),
-                        sslConfiguration.getPrivateKeyPassword());
+                Certificate clientCertificate = (Certificate) readPemObject(sslDomain.getCertificateFile(), null, Certificate.class);
+                Object keyOrKeyPair = readPemObject(
+                        sslDomain.getPrivateKeyFile(),
+                        sslDomain.getPrivateKeyPassword(), PrivateKey.class, KeyPair.class);
+
+                final PrivateKey clientPrivateKey;
+                if (keyOrKeyPair instanceof PrivateKey)
+                {
+                    clientPrivateKey = (PrivateKey)keyOrKeyPair;
+                }
+                else if (keyOrKeyPair instanceof KeyPair)
+                {
+                    clientPrivateKey = ((KeyPair)keyOrKeyPair).getPrivate();
+                }
+                else
+                {
+                    // Should not happen - readPemObject will have already verified key type
+                    throw new IllegalStateException("Unexpected key type " + keyOrKeyPair);
+                }
 
                 keystore.setKeyEntry(clientPrivateKeyAlias, clientPrivateKey,
                         dummyPassword, new Certificate[] { clientCertificate });
@@ -226,50 +294,79 @@ public class SslEngineFacadeFactory
     {
         List<String> newEnabled = new ArrayList<String>(currentEnabled);
 
-        boolean addedAnonymousCipherSuite = false;
+        int addedAnonymousCipherSuites = 0;
         for (String anonymousCipherSuiteName : anonymousCipherSuites)
         {
             if (supportedSuites.contains(anonymousCipherSuiteName))
             {
                 newEnabled.add(anonymousCipherSuiteName);
-                addedAnonymousCipherSuite = true;
+                addedAnonymousCipherSuites++;
             }
         }
 
-        if (!addedAnonymousCipherSuite)
+        if (addedAnonymousCipherSuites == 0)
         {
             throw new IllegalStateException("None of " + anonymousCipherSuites
                     + " anonymous cipher suites are within the supported list "
                     + supportedSuites);
         }
+
+        if(_logger.isLoggable(Level.FINE))
+        {
+            _logger.fine("There are now " + newEnabled.size()
+                    + " cipher suites enabled (previously " + currentEnabled.size()
+                    + "), including " + addedAnonymousCipherSuites + " out of the "
+                    + anonymousCipherSuites.size() + " requested anonymous ones." );
+        }
+
         return newEnabled;
     }
 
-    private Object readPemObject(String pemFile, final String privateKeyPassword)
+    private String engineToString(SSLEngine engine)
     {
-        PasswordFinder passwordFinder = new PasswordFinder()
+        return new StringBuilder("[ " )
+            .append(engine)
+            .append(", needClientAuth=").append(engine.getNeedClientAuth())
+            .append(", useClientMode=").append(engine.getUseClientMode())
+            .append(", peerHost=").append(engine.getPeerHost())
+            .append(", peerPort=").append(engine.getPeerPort())
+            .append(" ]").toString();
+    }
+
+    private Object readPemObject(String pemFile, String keyPassword, @SuppressWarnings("rawtypes") Class... expectedInterfaces)
+    {
+        final PasswordFinder passwordFinder;
+        if (keyPassword != null)
         {
-            @Override
-            public char[] getPassword()
-            {
-                return privateKeyPassword.toCharArray();
-            }
-        };
+            passwordFinder = getPasswordFinderFor(keyPassword);
+        }
+        else
+        {
+            passwordFinder = null;
+        }
 
         Reader reader = null;
         try
         {
             reader = new FileReader(pemFile);
-            return new PEMReader(reader, privateKeyPassword == null ? null : passwordFinder).readObject();
+            PEMReader pemReader = new PEMReader(reader, passwordFinder);
+            Object pemObject = pemReader.readObject();
+            if (!checkPemObjectIsOfAllowedTypes(pemObject, expectedInterfaces))
+            {
+                throw new IllegalStateException("File " + pemFile + " does not provide a object of the required type."
+                        + " Read an object of class " + pemObject.getClass().getName()
+                        + " whilst expecting an implementation of one of the following  : " + Arrays.asList(expectedInterfaces));
+            }
+            return pemObject;
         }
         catch(PEMException e)
         {
             _logger.log(Level.SEVERE, "Unable to read PEM object. Perhaps you need the unlimited strength libraries in <java-home>/jre/lib/security/ ?", e);
-            throw new RuntimeException(e);
+            throw new IllegalStateException("Unable to read PEM object from file " + pemFile, e);
         }
         catch (IOException e)
         {
-            throw new RuntimeException(e);
+            throw new RuntimeException("Unable to read PEM object from file " + pemFile, e);
         }
         finally
         {
@@ -287,9 +384,35 @@ public class SslEngineFacadeFactory
         }
     }
 
-    private Object readPemObject(String pemFile)
+    @SuppressWarnings("rawtypes")
+    private boolean checkPemObjectIsOfAllowedTypes(Object pemObject,  Class... expectedInterfaces)
+    {
+        if (expectedInterfaces.length == 0)
+        {
+            throw new IllegalArgumentException("Must be at least one expectedKeyTypes");
+        }
+
+        for (Class keyInterface : expectedInterfaces)
+        {
+            if (keyInterface.isInstance(pemObject))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private PasswordFinder getPasswordFinderFor(final String keyPassword)
     {
-        return readPemObject(pemFile, null);
+        PasswordFinder passwordFinder = new PasswordFinder()
+        {
+            @Override
+            public char[] getPassword()
+            {
+                return keyPassword.toCharArray();
+            }
+        };
+        return passwordFinder;
     }
 
     private final class AlwaysTrustingTrustManager implements X509TrustManager

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslImpl.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslImpl.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslImpl.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/engine/impl/ssl/SslImpl.java Tue Jan  8 08:29:31 2013
@@ -21,99 +21,32 @@
 package org.apache.qpid.proton.engine.impl.ssl;
 
 import org.apache.qpid.proton.engine.Ssl;
+import org.apache.qpid.proton.engine.SslDomain;
+import org.apache.qpid.proton.engine.SslPeerDetails;
 import org.apache.qpid.proton.engine.impl.TransportInput;
 import org.apache.qpid.proton.engine.impl.TransportOutput;
 import org.apache.qpid.proton.engine.impl.TransportWrapper;
 import org.apache.qpid.proton.engine.impl.PlainTransportWrapper;
 
-/*
- * Enables the JSSE system debugging system property:
- *
- *     -Djavax.net.debug=all
- */
 public class SslImpl implements Ssl
 {
-    private Mode _mode;
-    private VerifyMode _verifyMode = VerifyMode.ANONYMOUS_PEER;
-    private String _certificateFile;
-    private String _privateKeyFile;
-    private String _privateKeyPassword;
-    private String _trustedCaDb;
-    private boolean _allowUnsecuredClient;
-
     private SslTransportWrapper _unsecureClientAwareTransportWrapper;
 
-    @Override
-    public void init(Mode mode)
-    {
-        _mode = mode;
-    }
-
-    @Override
-    public void setCredentials(String certificateFile,
-            String privateKeyFile, String privateKeyPassword)
-    {
-        _certificateFile = certificateFile;
-        _privateKeyFile = privateKeyFile;
-        _privateKeyPassword = privateKeyPassword;
-    }
-
-    @Override
-    public void setTrustedCaDb(String certificateDb)
-    {
-        _trustedCaDb = certificateDb;
-    }
-
-    @Override
-    public String getTrustedCaDb()
-    {
-        return _trustedCaDb;
-    }
-
-    @Override
-    public void allowUnsecuredClient(boolean allowUnsecuredClient)
-    {
-        if (_mode == Mode.CLIENT)
-        {
-            throw new IllegalArgumentException("Only servers may allow unsecured clients");
-        }
-        _allowUnsecuredClient = allowUnsecuredClient;
-    }
-
-    @Override
-    public void setPeerAuthentication(VerifyMode verifyMode)
-    {
-        _verifyMode = verifyMode;
-    }
-
-    @Override
-    public VerifyMode getPeerAuthentication()
-    {
-        return _verifyMode;
-    }
-
-    @Override
-    public Mode getMode()
-    {
-        return _mode;
-    }
-
-    @Override
-    public String getPrivateKeyFile()
-    {
-        return _privateKeyFile;
-    }
+    private final SslDomain _domain;
+    private final ProtonSslEngineProvider _protonSslEngineProvider;
 
-    @Override
-    public String getPrivateKeyPassword()
-    {
-        return _privateKeyPassword;
-    }
+    private final SslPeerDetails _peerDetails;
 
-    @Override
-    public String getCertificateFile()
-    {
-        return _certificateFile;
+    /**
+     * @param sslDomain must implement {@link ProtonSslEngineProvider}. This is not possible
+     * enforce at the API level because {@link ProtonSslEngineProvider} is not part of the
+     * public Proton API.</p>
+     */
+    public SslImpl(SslDomain domain, SslPeerDetails peerDetails)
+    {
+        _domain = domain;
+        _protonSslEngineProvider = (ProtonSslEngineProvider)domain;
+        _peerDetails = peerDetails;
     }
 
     public TransportWrapper wrap(TransportInput inputProcessor, TransportOutput outputProcessor)
@@ -206,8 +139,12 @@ public class SslImpl implements Ssl
         {
             if (_transportWrapper == null)
             {
-                SslTransportWrapper sslTransportWrapper = new SimpleSslTransportWrapper(SslImpl.this, _inputProcessor, _outputProcessor);
-                if (_allowUnsecuredClient)
+                SslTransportWrapper sslTransportWrapper = new SimpleSslTransportWrapper(
+                        _protonSslEngineProvider.createSslEngine(_peerDetails),
+                        _inputProcessor,
+                        _outputProcessor);
+
+                if (_domain.allowUnsecuredClient())
                 {
                     TransportWrapper plainTransportWrapper = new PlainTransportWrapper(_outputProcessor, _inputProcessor);
                     _transportWrapper = new SslHandshakeSniffingTransportWrapper(sslTransportWrapper, plainTransportWrapper);

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/message/impl/MessageImpl.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/message/impl/MessageImpl.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/message/impl/MessageImpl.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/message/impl/MessageImpl.java Tue Jan  8 08:29:31 2013
@@ -28,10 +28,8 @@ import org.apache.qpid.proton.amqp.Symbo
 import org.apache.qpid.proton.amqp.UnsignedByte;
 import org.apache.qpid.proton.amqp.UnsignedInteger;
 import org.apache.qpid.proton.amqp.messaging.*;
-import org.apache.qpid.proton.codec.AMQPDefinedTypes;
-import org.apache.qpid.proton.codec.DecoderImpl;
-import org.apache.qpid.proton.codec.EncoderImpl;
-import org.apache.qpid.proton.codec.WritableBuffer;
+import org.apache.qpid.proton.amqp.messaging.Data;
+import org.apache.qpid.proton.codec.*;
 import org.apache.qpid.proton.message.*;
 
 public class MessageImpl implements Message
@@ -675,6 +673,17 @@ public class MessageImpl implements Mess
         return encode(new WritableBuffer.ByteBufferWrapper(buffer));
     }
 
+    public int encode2(byte[] data, int offset, int length)
+    {
+        ByteBuffer buffer = ByteBuffer.wrap(data, offset, length);
+        WritableBuffer.ByteBufferWrapper first = new WritableBuffer.ByteBufferWrapper(buffer);
+        DroppingWritableBuffer second = new DroppingWritableBuffer();
+        CompositeWritableBuffer composite = new CompositeWritableBuffer(first, second);
+        int start = composite.position();
+        encode(composite);
+        return composite.position() - start;
+    }
+
     public int encode(WritableBuffer buffer)
     {
         int length = buffer.remaining();

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/MessengerImpl.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/MessengerImpl.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/MessengerImpl.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/MessengerImpl.java Tue Jan  8 08:29:31 2013
@@ -43,7 +43,6 @@ import org.apache.qpid.proton.driver.imp
 import org.apache.qpid.proton.engine.impl.ConnectionImpl;
 import org.apache.qpid.proton.message.Message;
 import org.apache.qpid.proton.message.impl.MessageImpl;
-import org.apache.qpid.proton.messenger.AcceptMode;
 import org.apache.qpid.proton.messenger.Messenger;
 import org.apache.qpid.proton.messenger.MessengerException;
 import org.apache.qpid.proton.messenger.Status;
@@ -68,7 +67,6 @@ public class MessengerImpl implements Me
     private Driver _driver;
     private int _credit;
     private int _distributed;
-    private AcceptMode _acceptMode = AcceptMode.AUTO;
     private TrackerQueue _incoming = new TrackerQueue();
     private TrackerQueue _outgoing = new TrackerQueue();
 
@@ -107,6 +105,7 @@ public class MessengerImpl implements Me
             try
             {
                 c.process();
+                c.close();
             }
             catch (IOException e)
             {
@@ -194,9 +193,6 @@ public class MessengerImpl implements Me
                     Message message = new MessageImpl();
                     message.decode(_buffer, 0, size);
                     _incoming.add(delivery);
-                    if (_acceptMode == AcceptMode.AUTO) {
-                        _incoming.accept(incomingTracker());
-                    }
                     _distributed--;
                     delivery.getLink().advance();
                     return message;
@@ -250,15 +246,6 @@ public class MessengerImpl implements Me
     }
 
 
-    public AcceptMode getAcceptMode()
-    {
-        return _acceptMode;
-    }
-    public void setAcceptMode(AcceptMode mode)
-    {
-        _acceptMode = mode;
-    }
-
     public int getIncomingWindow()
     {
         return _incoming.getWindow();

Modified: qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/TrackerQueue.java
URL: http://svn.apache.org/viewvc/qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/TrackerQueue.java?rev=1430167&r1=1430166&r2=1430167&view=diff
==============================================================================
--- qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/TrackerQueue.java (original)
+++ qpid/proton/branches/jni-binding/proton-j/proton/src/main/java/org/apache/qpid/proton/messenger/impl/TrackerQueue.java Tue Jan  8 08:29:31 2013
@@ -77,6 +77,7 @@ class TrackerQueue
         }
         int sequence = _hwm++;
         _deliveries.add(delivery);
+        slide();
     }
 
     Status getStatus(Tracker tracker)
@@ -139,7 +140,7 @@ class TrackerQueue
                 Delivery d = _deliveries.get(0);
                 if (d.getLocalState() == null)
                 {
-                    return;
+                    d.disposition(ACCEPTED);
                 }
 
                 d.settle();
@@ -186,7 +187,6 @@ class TrackerQueue
                 operation.apply(d);
             }
         }
-        slide();
     }
 
     private static interface DeliveryOperation



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org