qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From astitc...@apache.org
Subject [2/4] qpid-proton git commit: PROTON-334: SASL Implementation for Proton-C using Cyrus SASL
Date Tue, 21 Apr 2015 07:10:52 GMT
PROTON-334: SASL Implementation for Proton-C using Cyrus SASL

This work Adds some new APIs to the transport and connection
objects to make a higher level abstraction for authentication.
This generally makes it much easier to use authentication.

It also vastly changes the Proton C API for SASL and deprecates
nearly all of the previous interface that allowed reading and
writing individual SASL frames.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/4a09c6a1
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/4a09c6a1
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/4a09c6a1

Branch: refs/heads/master
Commit: 4a09c6a17f865df10f53fa61c8d2bc88d4627bb0
Parents: 450b8ba
Author: Andrew Stitcher <astitcher@apache.org>
Authored: Thu Apr 9 02:14:14 2015 -0400
Committer: Andrew Stitcher <astitcher@apache.org>
Committed: Thu Apr 9 02:14:14 2015 -0400

----------------------------------------------------------------------
 proton-c/CMakeLists.txt                         |  24 +-
 proton-c/bindings/python/cproton.i              |  14 -
 proton-c/bindings/python/proton/__init__.py     |  74 +-
 proton-c/include/proton/connection.h            |  39 +
 proton-c/include/proton/cproton.i               |  67 +-
 proton-c/include/proton/event.h                 |   8 +
 proton-c/include/proton/sasl.h                  | 149 ++--
 proton-c/include/proton/transport.h             |  67 ++
 proton-c/src/engine/engine-internal.h           |  11 +
 proton-c/src/engine/engine.c                    |  22 +
 proton-c/src/events/event.c                     |   2 +
 proton-c/src/messenger/messenger.c              |  16 +-
 proton-c/src/reactor/acceptor.c                 |   4 -
 proton-c/src/reactor/connection.c               |   2 -
 proton-c/src/sasl/cyrus_sasl.c                  | 798 +++++++++++++++++++
 proton-c/src/sasl/none_sasl.c                   | 425 ++++++++++
 proton-c/src/sasl/sasl-internal.h               |   8 +-
 proton-c/src/sasl/sasl.c                        | 445 +----------
 proton-c/src/ssl/openssl.c                      |   7 +
 proton-c/src/tests/reactor.c                    |   4 +-
 proton-c/src/transport/transport.c              |  96 ++-
 .../qpid/proton/engine/impl/SaslImpl.java       |   7 +-
 proton-j/src/main/resources/csasl.py            |  48 +-
 tests/python/proton_tests/sasl.py               | 117 ++-
 tests/python/proton_tests/transport.py          |   5 +-
 25 files changed, 1681 insertions(+), 778 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index 4073f98..1b3f6d3 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -17,6 +17,7 @@
 # under the License.
 #
 
+include(CheckIncludeFiles)
 include(CheckLibraryExists)
 include(CheckSymbolExists)
 
@@ -129,6 +130,25 @@ else (CLOCK_GETTIME_IN_LIBC)
   endif (CLOCK_GETTIME_IN_RT)
 endif (CLOCK_GETTIME_IN_LIBC)
 
+# See if Cyrus SASL is desired and available
+CHECK_LIBRARY_EXISTS (sasl2 sasl_checkpass "" FOUND_SASL_LIB)
+CHECK_INCLUDE_FILES (sasl/sasl.h FOUND_SASL_H)
+find_package_handle_standard_args(SASL DEFAULT_MSG FOUND_SASL_LIB FOUND_SASL_H)
+set(sasl_providers cyrus none)
+if (SASL_FOUND)
+  set (sasl_impl cyrus)
+else (SASL_FOUND)
+  set (sasl_impl none)
+endif (SASL_FOUND)
+set(SASL_IMPL ${sasl_impl} CACHE STRING "Library to use for SSL/TLS support. Valid values: ${sasl_providers}")
+
+if (SASL_IMPL STREQUAL cyrus)
+  set(pn_sasl_impl src/sasl/sasl.c src/sasl/cyrus_sasl.c)
+  set(SASL_LIB "sasl2")
+elseif (SASL_IMPL STREQUAL none)
+  set(pn_sasl_impl src/sasl/sasl.c src/sasl/none_sasl.c)
+endif ()
+
 CHECK_SYMBOL_EXISTS(uuid_generate "uuid/uuid.h" UUID_GENERATE_IN_LIBC)
 if (UUID_GENERATE_IN_LIBC)
   list(APPEND PLATFORM_DEFINITIONS "USE_UUID_GENERATE")
@@ -269,6 +289,7 @@ set (qpid-proton-platform
   ${pn_io_impl}
   ${pn_selector_impl}
   src/platform.c
+  ${pn_sasl_impl}
   ${pn_ssl_impl}
   )
 
@@ -301,7 +322,6 @@ set (qpid-proton-core
   src/transport/autodetect.c
   src/transport/transport.c
   src/message/message.c
-  src/sasl/sasl.c
 
   src/reactor/reactor.c
   src/reactor/handler.c
@@ -343,7 +363,7 @@ add_library (
   ${qpid-proton-platform}
   )
 
-target_link_libraries (qpid-proton ${UUID_LIB} ${SSL_LIB} ${TIME_LIB} ${PLATFORM_LIBS})
+target_link_libraries (qpid-proton ${UUID_LIB} ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS})
 
 set_target_properties (
   qpid-proton

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/bindings/python/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/cproton.i b/proton-c/bindings/python/cproton.i
index 8efc2aa..70d6c42 100644
--- a/proton-c/bindings/python/cproton.i
+++ b/proton-c/bindings/python/cproton.i
@@ -158,20 +158,6 @@ ssize_t pn_data_decode(pn_data_t *data, char *STRING, size_t LENGTH);
 %}
 %ignore pn_data_encode;
 
-%rename(pn_sasl_recv) wrap_pn_sasl_recv;
-%inline %{
-  int wrap_pn_sasl_recv(pn_sasl_t *sasl, char *OUTPUT, size_t *OUTPUT_SIZE) {
-    ssize_t sz = pn_sasl_recv(sasl, OUTPUT, *OUTPUT_SIZE);
-    if (sz >= 0) {
-      *OUTPUT_SIZE = sz;
-    } else {
-      *OUTPUT_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_sasl_recv;
-
 int pn_data_format(pn_data_t *data, char *OUTPUT, size_t *OUTPUT_SIZE);
 %ignore pn_data_format;
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/bindings/python/proton/__init__.py
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/proton/__init__.py b/proton-c/bindings/python/proton/__init__.py
index eeb7768..8a9d857 100644
--- a/proton-c/bindings/python/proton/__init__.py
+++ b/proton-c/bindings/python/proton/__init__.py
@@ -2401,6 +2401,20 @@ class Connection(Wrapper, Endpoint):
 
   hostname = property(_get_hostname, _set_hostname)
 
+  def _get_user(self):
+    return utf82unicode(pn_connection_get_user(self._impl))
+  def _set_user(self, name):
+    return pn_connection_set_user(self._impl, unicode2utf8(name))
+
+  user = property(_get_user, _set_user)
+
+  def _get_password(self):
+    return None
+  def _set_password(self, name):
+    return pn_connection_set_password(self._impl, unicode2utf8(name))
+
+  user = property(_get_password, _set_password)
+
   @property
   def remote_container(self):
     """The container identifier specified by the remote peer for this connection."""
@@ -3162,6 +3176,20 @@ class Transport(Wrapper):
     else:
       return err
 
+  def require_auth(self, bool):
+    pn_transport_require_auth(self._impl, bool)
+
+  @property
+  def authenticated(self):
+    return pn_transport_is_authenticated(self._impl)
+
+  def require_encryption(self, bool):
+    pn_transport_require_encryption(self._impl, bool)
+
+  @property
+  def encrypted(self):
+    return pn_transport_is_encrypted(self._impl)
+
   def bind(self, connection):
     """Assign a connection to the transport"""
     self._check(pn_transport_bind(self._impl, connection._impl))
@@ -3298,7 +3326,6 @@ class SASL(Wrapper):
 
   OK = PN_SASL_OK
   AUTH = PN_SASL_AUTH
-  SKIPPED = PN_SASL_SKIPPED
 
   def __init__(self, transport):
     Wrapper.__init__(self, transport._impl, pn_transport_attachments)
@@ -3311,39 +3338,6 @@ class SASL(Wrapper):
     else:
       return err
 
-  def mechanisms(self, mechs):
-    pn_sasl_mechanisms(self._sasl, mechs)
-
-  # @deprecated
-  def client(self):
-    pn_sasl_client(self._sasl)
-
-  # @deprecated
-  def server(self):
-    pn_sasl_server(self._sasl)
-
-  def allow_skip(self, allow):
-    pn_sasl_allow_skip(self._sasl, allow)
-
-  def plain(self, user, password):
-    pn_sasl_plain(self._sasl, user, password)
-
-  def send(self, data):
-    self._check(pn_sasl_send(self._sasl, data, len(data)))
-
-  def recv(self):
-    sz = 16
-    while True:
-      n, data = pn_sasl_recv(self._sasl, sz)
-      if n == PN_OVERFLOW:
-        sz *= 2
-        continue
-      elif n == PN_EOS:
-        return None
-      else:
-        self._check(n)
-        return data
-
   @property
   def outcome(self):
     outcome = pn_sasl_outcome(self._sasl)
@@ -3352,18 +3346,12 @@ class SASL(Wrapper):
     else:
       return outcome
 
+  def allowed_mechs(self, mechs):
+    pn_sasl_allowed_mechs(self._sasl, mechs)
+
   def done(self, outcome):
     pn_sasl_done(self._sasl, outcome)
 
-  STATE_IDLE = PN_SASL_IDLE
-  STATE_STEP = PN_SASL_STEP
-  STATE_PASS = PN_SASL_PASS
-  STATE_FAIL = PN_SASL_FAIL
-
-  @property
-  def state(self):
-    return pn_sasl_state(self._sasl)
-
 
 class SSLException(TransportException):
   pass

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/include/proton/connection.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection.h b/proton-c/include/proton/connection.h
index fcaddf7..efc5a60 100644
--- a/proton-c/include/proton/connection.h
+++ b/proton-c/include/proton/connection.h
@@ -276,6 +276,45 @@ PN_EXTERN const char *pn_connection_get_container(pn_connection_t *connection);
 PN_EXTERN void pn_connection_set_container(pn_connection_t *connection, const char *container);
 
 /**
+ * Set the authentication username for a client connection
+ *
+ * It is necessary to set the username and password before binding the connection
+ * to a trasnport and it isn't allowed to change them after the binding.
+ *
+ * If not set then no authentication will be negotiated unless the client
+ * sasl layer is explicitly created (this would be for sometting like Kerberos
+ * where the credentials are implicit in the environment, or to explicitly use
+ * the ANONYMOUS SASL mechanism)
+ *
+ * @param[in] connection the connection
+ * @param[in] user the username
+ */
+PN_EXTERN void pn_connection_set_user(pn_connection_t *connection, const char *user);
+
+/**
+ * Set the authentication password for a client connection
+ *
+ * It is necessary to set the username and password before binding the connection
+ * to a trasnport and it isn't allowed to change them after the binding.
+ *
+ * Note that the password is write only and has no accessor as the underlying
+ * implementation should be zeroing the password after use to avoid the password
+ * being present in memory longer than necessary
+ *
+ * @param[in] connection the connection
+ * @param[in] password the password corresponding to the username - this will be copied and zeroed out after use
+ */
+PN_EXTERN void pn_connection_set_password(pn_connection_t *connection, const char *password);
+
+/**
+ * Get the authentication username for a client connection
+ *
+ * @param[in] connection the connection
+ * @return the username passed into the connection
+ */
+PN_EXTERN const char *pn_connection_get_user(pn_connection_t *connection);
+
+/**
  * Get the value of the AMQP Hostname used by a connection object.
  *
  * The pointer returned by this operation is valid until

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/include/proton/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/cproton.i b/proton-c/include/proton/cproton.i
index 844d763..ac2b121 100644
--- a/proton-c/include/proton/cproton.i
+++ b/proton-c/include/proton/cproton.i
@@ -81,11 +81,7 @@ typedef unsigned long int uintptr_t;
 
 %aggregate_check(int, check_sasl_outcome,
                  PN_SASL_NONE, PN_SASL_OK, PN_SASL_AUTH,
-                 PN_SASL_SYS, PN_SASL_PERM, PN_SASL_TEMP, PN_SASL_SKIPPED);
-
-%aggregate_check(int, check_sasl_state,
-                 PN_SASL_IDLE, PN_SASL_STEP,
-                 PN_SASL_PASS, PN_SASL_FAIL);
+                 PN_SASL_SYS, PN_SASL_PERM, PN_SASL_TEMP);
 
 
 %contract pn_code(int code)
@@ -881,71 +877,12 @@ typedef unsigned long int uintptr_t;
   pn_sasl != NULL;
 }
 
-%contract pn_sasl_state(pn_sasl_t *sasl)
-{
- require:
-  sasl != NULL;
- ensure:
-  check_sasl_state(pn_sasl_state);
-}
-
-%contract pn_sasl_mechanisms(pn_sasl_t *sasl, const char *mechanisms)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_remote_mechanisms(pn_sasl_t *sasl)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_client(pn_sasl_t *sasl)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_server(pn_sasl_t *sasl)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_plain(pn_sasl_t *sasl, const char *username, const char *password)
+%contract pn_sasl_allowed_mechs(pn_sasl_t *sasl, const char *mechanisms)
 {
  require:
   sasl != NULL;
 }
 
-%contract pn_sasl_pending(pn_sasl_t *sasl)
-{
- require:
-  sasl != NULL;
-}
-
-%contract pn_sasl_recv(pn_sasl_t *sasl, char *bytes, size_t size)
-{
- require:
-  sasl != NULL;
-  bytes != NULL;
-  size > 0;
-}
-
-%contract pn_sasl_send(pn_sasl_t *sasl, const char *bytes, size_t size)
-{
- require:
-  sasl != NULL;
-  bytes != NULL;
-  size > 0;
-}
 
 %contract pn_sasl_done(pn_sasl_t *sasl, pn_sasl_outcome_t outcome)
 {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/include/proton/event.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/event.h b/proton-c/include/proton/event.h
index 38aa121..ae559cc 100644
--- a/proton-c/include/proton/event.h
+++ b/proton-c/include/proton/event.h
@@ -263,6 +263,14 @@ typedef enum {
   PN_TRANSPORT,
 
   /**
+   * The transport has authenticated, if this is received by a server
+   * the associated transport has authenticated an incoming connection
+   * and pn_transport_get_user() can be used to obtain the authenticated
+   * user.
+   */
+  PN_TRANSPORT_AUTHENTICATED,
+
+  /**
    * Indicates that a transport error has occurred. Use
    * ::pn_transport_condition() to access the details of the error
    * from the associated transport.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/include/proton/sasl.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/sasl.h b/proton-c/include/proton/sasl.h
index 104e156..d9600b4 100644
--- a/proton-c/include/proton/sasl.h
+++ b/proton-c/include/proton/sasl.h
@@ -55,136 +55,101 @@ typedef enum {
   PN_SASL_SYS=2,    /** failed due to a system error */
   PN_SASL_PERM=3,   /** failed due to unrecoverable error */
   PN_SASL_TEMP=4,   /** failed due to transient error */
-  PN_SASL_SKIPPED=5 /** the peer didn't perform the sasl exchange */
 } pn_sasl_outcome_t;
 
-/** The state of the SASL negotiation process */
-typedef enum {
-  PN_SASL_IDLE,    /** Pending SASL Init */
-  PN_SASL_STEP,    /** negotiation in progress */
-  PN_SASL_PASS,    /** negotiation completed successfully */
-  PN_SASL_FAIL     /** negotiation failed */
-} pn_sasl_state_t;
-
 /** Construct an Authentication and Security Layer object
  *
- * @return a new SASL object representing the layer.
+ * This will return the SASL layer object for the supplied transport
+ * object. If there is currently no SASL layer one will be created.
+ *
+ * On the client side of an AMQP connection this will have the effect
+ * of ensuring that the AMQP SASL layer is used for that connection.
+ *
+ * @return an object representing the SASL layer.
  */
 PN_EXTERN pn_sasl_t *pn_sasl(pn_transport_t *transport);
 
-/** Access the current state of the layer.
+/** Set the outcome of SASL negotiation
  *
- * @param[in] sasl the layer to retrieve the state from.
- * @return The state of the sasl layer.
- */
-PN_EXTERN pn_sasl_state_t pn_sasl_state(pn_sasl_t *sasl);
-
-/** Set the acceptable SASL mechanisms for the layer.
+ * Used by the server to set the result of the negotiation process.
  *
- * @param[in] sasl the layer to update
- * @param[in] mechanisms a list of acceptable SASL mechanisms,
- *                       separated by space
+ * @todo
  */
-PN_EXTERN void pn_sasl_mechanisms(pn_sasl_t *sasl, const char *mechanisms);
+PN_EXTERN void pn_sasl_done(pn_sasl_t *sasl, pn_sasl_outcome_t outcome);
 
-/** Retrieve the list of SASL mechanisms provided by the remote.
+/** Retrieve the outcome of SASL negotiation.
  *
- * @param[in] sasl the SASL layer.
- * @return a string containing a list of the SASL mechanisms
- *         advertised by the remote (separated by spaces)
+ * @todo
  */
-PN_EXTERN const char *pn_sasl_remote_mechanisms(pn_sasl_t *sasl);
+PN_EXTERN pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl);
 
-/**
- * @deprecated
- * Configure the SASL layer to act as a SASL client.
+/** Retrieve the authenticated user
  *
- * This is now unnecessary, and performs no function. The server/clientness
- * of the sasl layer is determined from the role of the transport that is used to create
- * it. The API is retained here so as not to break existing code.
+ * This is usually used at the the server end to find the name of the authenticated user.
+ * On the client it will merely return whatever user was passed in to the
+ * pn_transport_set_user_password() API.
  *
- * @param[in] sasl the SASL layer to configure as a client
- */
-PN_EXTERN void pn_sasl_client(pn_sasl_t *sasl);
-
-/**
- * @deprecated
- * Configure the SASL layer to act as a server.
+ * If pn_sasl_outcome() returns a value other than PN_SASL_OK, then there will be no user to return.
+ * The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
  *
- * This is now only necessary for backwards compatibility if creating a server pn_sasl_t
- * from a pn_transport_t which was created implicitly as a client by pn_transport().
+ * @param[in] sasl the sasl layer
  *
- * @param[in] sasl the SASL layer to configure as a server
+ * @return
+ * If the SASL layer was not negotiated then 0 is returned
+ * If the ANONYMOUS mechanism is used then the user will be "anonymous"
+ * Otherwise a string containing the user is returned.
  */
-PN_EXTERN void pn_sasl_server(pn_sasl_t *sasl);
+PN_EXTERN const char *pn_sasl_get_user(pn_sasl_t *sasl);
 
-/** Configure a SASL server layer to permit the client to skip the SASL exchange.
+/** Return the selected SASL mechanism
  *
- * If the peer client skips the SASL exchange (i.e. goes right to the AMQP header)
- * this server layer will succeed and result in the outcome of PN_SASL_SKIPPED.
- * The default behavior is to fail and close the connection if the client skips
- * SASL.
+ * The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
  *
- * @param[in] sasl the SASL layer to configure
- * @param[in] allow true -> allow skip; false -> forbid skip
- */
-PN_EXTERN void pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow);
-
-/** Configure the SASL layer to use the "PLAIN" mechanism.
- *
- * A utility function to configure a simple client SASL layer using
- * PLAIN authentication.
+ * @param[in] sasl the SASL layer
  *
- * @param[in] sasl the layer to configure.
- * @param[in] username credential for the PLAIN authentication
- *                     mechanism
- * @param[in] password credential for the PLAIN authentication
- *                     mechanism
+ * @return The authentication mechanism selected by the SASL layer
  */
-PN_EXTERN void pn_sasl_plain(pn_sasl_t *sasl, const char *username, const char *password);
+PN_EXTERN const char *pn_sasl_get_mech(pn_sasl_t *sasl);
 
-/** Determine the size of the bytes available via pn_sasl_recv().
+/** SASL mechanism that are not to be considered for authentication
  *
- * Returns the size in bytes available via pn_sasl_recv().
+ * This can be used on either the client or the server to restrict the SASL mechanisms that may be used.
  *
- * @param[in] sasl the SASL layer.
- * @return The number of bytes available, zero if no available data.
+ * @param[in] sasl the SASL layer
+ * @param[in] mechs space separated list of mechanisms that are not to be allowed for authentication
  */
-PN_EXTERN size_t pn_sasl_pending(pn_sasl_t *sasl);
+PN_EXTERN void pn_sasl_allowed_mechs(pn_sasl_t *sasl, const char *mechs);
 
-/** Read challenge/response data sent from the peer.
+/**
+ * Set the sasl configuration name
  *
- * Use pn_sasl_pending to determine the size of the data.
+ * This is used to construct the SASL configuration filename. In the current implementation
+ * it ".conf" is added to the name and the file is looked for in the configuration directory.
  *
- * @param[in] sasl the layer to read from.
- * @param[out] bytes written with up to size bytes of inbound data.
- * @param[in] size maximum number of bytes that bytes can accept.
- * @return The number of bytes written to bytes, or an error code if < 0.
- */
-PN_EXTERN ssize_t pn_sasl_recv(pn_sasl_t *sasl, char *bytes, size_t size);
-
-/** Send challenge or response data to the peer.
+ * If not set it will default to "proton-server" for a sasl server and "proton-client"
+ * for a client.
  *
- * @param[in] sasl The SASL layer.
- * @param[in] bytes The challenge/response data.
- * @param[in] size The number of data octets in bytes.
- * @return The number of octets read from bytes, or an error code if < 0
+ * @param[in] sasl the SASL layer
+ * @param[in] name the configuration name
  */
-PN_EXTERN ssize_t pn_sasl_send(pn_sasl_t *sasl, const char *bytes, size_t size);
+PN_EXTERN void pn_sasl_config_name(pn_sasl_t *sasl, const char *name);
 
-/** Set the outcome of SASL negotiation
+/**
+ * Set the sasl configuration path
  *
- * Used by the server to set the result of the negotiation process.
+ * This is used to tell SASL where to look for the configuration file.
+ * In the current implementation it can be a colon separated list of directories.
  *
- * @todo
- */
-PN_EXTERN void pn_sasl_done(pn_sasl_t *sasl, pn_sasl_outcome_t outcome);
-
-/** Retrieve the outcome of SASL negotiation.
+ * The environment variable PN_SASL_CONFIG_PATH can also be used to set this path,
+ * but if both methods are used then this pn_sasl_config_path() will take precedence.
  *
- * @todo
+ * If not set the underlying implementation default will be used.
+ * for a client.
+ *
+ * @param[in] sasl the SASL layer
+ * @param[in] path the configuration path
  */
-PN_EXTERN pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl);
+PN_EXTERN void pn_sasl_config_path(pn_sasl_t *sasl, const char *path);
 
 /** @} */
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/include/proton/transport.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/transport.h b/proton-c/include/proton/transport.h
index 690952b..90244d5 100644
--- a/proton-c/include/proton/transport.h
+++ b/proton-c/include/proton/transport.h
@@ -121,6 +121,73 @@ PN_EXTERN void pn_transport_set_server(pn_transport_t *transport);
  */
 PN_EXTERN void pn_transport_free(pn_transport_t *transport);
 
+/** Retrieve the authenticated user
+ *
+ * This is usually used at the the server end to find the name of the authenticated user.
+ * On the client it will merely return whatever user was passed in to the
+ * pn_transport_set_user_password() API.
+ *
+ * The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
+ *
+ * @param[in] transport the transport
+ *
+ * @return
+ * If a the user is anonymous (either no SASL layer is negotiated or the SASL ANONYMOUS mechanism is used)
+ * then the user will be "anonymous"
+ * Otherwise a string containing the user is returned.
+ */
+PN_EXTERN const char *pn_transport_get_user(pn_transport_t *transport);
+
+/**
+ * Set whether a non authenticated transport connection is allowed
+ *
+ * There are several ways within the AMQP protocol suite to get unauthenticated connections:
+ * - Use no SASL layer (with either no TLS or TLS without client certificates)
+ * - Use an SASL layer but the ANONYMOUS mechanism
+ *
+ * The default if this option is not set is to allow unauthenticated connections.
+ *
+ * @param[in] transport the transport
+ * @param[in] required boolean is true when authenticated connections are required
+ */
+PN_EXTERN void pn_transport_require_auth(pn_transport_t *transport, bool required);
+
+/**
+ * Tell whether the transport connection is authenticated
+ *
+ * Note that this property may not be stable until the PN_CONNECTION_REMOTE_OPEN
+ * event is received.
+ *
+ * @param[in] transport the transport
+ * @return bool representing authentication
+ */
+PN_EXTERN bool pn_transport_is_authenticated(pn_transport_t *transport);
+
+/**
+ * Set whether a non encrypted transport connection is allowed
+ *
+ * There are several ways within the AMQP protocol suite to get encrypted connections:
+ * - Use TLS/SSL
+ * - Use an SASL with a mechanism that supports saecurity layers
+ *
+ * The default if this option is not set is to allow unencrypted connections.
+ *
+ * @param[in] transport the transport
+ * @param[in] required boolean is true when encrypted connections are required
+ */
+PN_EXTERN void pn_transport_require_encryption(pn_transport_t *transport, bool required);
+
+/**
+ * Tell whether the transport connection is encrypted
+ *
+ * Note that this property may not be stable until the PN_CONNECTION_REMOTE_OPEN
+ * event is received.
+ *
+ * @param[in] transport the transport
+ * @return bool representing encryption
+ */
+PN_EXTERN bool pn_transport_is_encrypted(pn_transport_t *transport);
+
 /**
  * Get additional information about the condition of the transport.
  *

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/engine/engine-internal.h
----------------------------------------------------------------------
diff --git a/proton-c/src/engine/engine-internal.h b/proton-c/src/engine/engine-internal.h
index e5ec602..035d5be 100644
--- a/proton-c/src/engine/engine-internal.h
+++ b/proton-c/src/engine/engine-internal.h
@@ -194,8 +194,16 @@ struct pn_transport_t {
   bool posted_idle_timeout;
   bool server;
   bool halt;
+  bool auth_required;
+  bool authenticated;
+  bool encryption_required;
+  bool encrypted;
 
   bool referenced;
+
+  // Temporarily here until refactor
+  bool sasl_input_bypass;
+  bool sasl_output_bypass;
 };
 
 struct pn_connection_t {
@@ -213,6 +221,8 @@ struct pn_connection_t {
   pn_delivery_t *tpwork_tail;
   pn_string_t *container;
   pn_string_t *hostname;
+  pn_string_t *auth_user;
+  pn_string_t *auth_password;
   pn_data_t *offered_capabilities;
   pn_data_t *desired_capabilities;
   pn_data_t *properties;
@@ -331,6 +341,7 @@ void pn_clear_modified(pn_connection_t *connection, pn_endpoint_t *endpoint);
 void pn_connection_bound(pn_connection_t *conn);
 void pn_connection_unbound(pn_connection_t *conn);
 int pn_do_error(pn_transport_t *transport, const char *condition, const char *fmt, ...);
+void pn_set_error_layer(pn_transport_t *transport);
 void pn_session_bound(pn_session_t* ssn);
 void pn_session_unbound(pn_session_t* ssn);
 void pn_link_bound(pn_link_t* link);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/engine/engine.c
----------------------------------------------------------------------
diff --git a/proton-c/src/engine/engine.c b/proton-c/src/engine/engine.c
index 5e05cbc..2454aa2 100644
--- a/proton-c/src/engine/engine.c
+++ b/proton-c/src/engine/engine.c
@@ -474,6 +474,8 @@ static void pn_connection_finalize(void *object)
 
   pn_free(conn->container);
   pn_free(conn->hostname);
+  pn_free(conn->auth_user);
+  pn_free(conn->auth_password);
   pn_free(conn->offered_capabilities);
   pn_free(conn->desired_capabilities);
   pn_free(conn->properties);
@@ -506,6 +508,8 @@ pn_connection_t *pn_connection(void)
   conn->tpwork_tail = NULL;
   conn->container = pn_string(NULL);
   conn->hostname = pn_string(NULL);
+  conn->auth_user = pn_string(NULL);
+  conn->auth_password = pn_string(NULL);
   conn->offered_capabilities = pn_data(0);
   conn->desired_capabilities = pn_data(0);
   conn->properties = pn_data(0);
@@ -568,6 +572,24 @@ void pn_connection_set_hostname(pn_connection_t *connection, const char *hostnam
   pn_string_set(connection->hostname, hostname);
 }
 
+const char *pn_connection_get_user(pn_connection_t *connection)
+{
+    assert(connection);
+    return pn_string_get(connection->auth_user);
+}
+
+void pn_connection_set_user(pn_connection_t *connection, const char *user)
+{
+    assert(connection);
+    pn_string_set(connection->auth_user, user);
+}
+
+void pn_connection_set_password(pn_connection_t *connection, const char *password)
+{
+    assert(connection);
+    pn_string_set(connection->auth_password, password);
+}
+
 pn_data_t *pn_connection_offered_capabilities(pn_connection_t *connection)
 {
   assert(connection);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/events/event.c
----------------------------------------------------------------------
diff --git a/proton-c/src/events/event.c b/proton-c/src/events/event.c
index 6d24f0f..611c61b 100644
--- a/proton-c/src/events/event.c
+++ b/proton-c/src/events/event.c
@@ -327,6 +327,8 @@ const char *pn_event_type_name(pn_event_type_t type)
     return "PN_DELIVERY";
   case PN_TRANSPORT:
     return "PN_TRANSPORT";
+  case PN_TRANSPORT_AUTHENTICATED:
+    return "PN_TRANSPORT_AUTHENTICATED";
   case PN_TRANSPORT_ERROR:
     return "PN_TRANSPORT_ERROR";
   case PN_TRANSPORT_HEAD_CLOSED:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/messenger/messenger.c
----------------------------------------------------------------------
diff --git a/proton-c/src/messenger/messenger.c b/proton-c/src/messenger/messenger.c
index abc76a4..f226f7b 100644
--- a/proton-c/src/messenger/messenger.c
+++ b/proton-c/src/messenger/messenger.c
@@ -330,10 +330,6 @@ static void pni_listener_readable(pn_selectable_t *sel)
 
   pn_ssl_t *ssl = pn_ssl(t);
   pn_ssl_init(ssl, ctx->domain, NULL);
-  pn_sasl_t *sasl = pn_sasl(t);
-
-  pn_sasl_mechanisms(sasl, "ANONYMOUS");
-  pn_sasl_done(sasl, PN_SASL_OK);
 
   pn_connection_t *conn = pn_messenger_connection(ctx->messenger, sock, scheme, NULL, NULL, NULL, NULL, ctx);
   pn_transport_bind(t, conn);
@@ -916,19 +912,9 @@ static int pn_transport_config(pn_messenger_t *messenger,
     }
     pn_ssl_t *ssl = pn_ssl(transport);
     pn_ssl_init(ssl, d, NULL);
-    pn_ssl_set_peer_hostname(ssl, pn_connection_get_hostname(connection));
     pn_ssl_domain_free( d );
   }
 
-  if (ctx->user) {
-    pn_sasl_t *sasl = pn_sasl(transport);
-    if (ctx->pass) {
-      pn_sasl_plain(sasl, ctx->user, ctx->pass);
-    } else {
-      pn_sasl_mechanisms(sasl, "ANONYMOUS");
-    }
-  }
-
   return 0;
 }
 
@@ -1073,6 +1059,8 @@ pn_connection_t *pn_messenger_connection(pn_messenger_t *messenger,
 
   pn_connection_set_container(connection, messenger->name);
   pn_connection_set_hostname(connection, host);
+  pn_connection_set_user(connection, user);
+  pn_connection_set_password(connection, pass);
 
   pn_list_add(messenger->connections, connection);
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/reactor/acceptor.c
----------------------------------------------------------------------
diff --git a/proton-c/src/reactor/acceptor.c b/proton-c/src/reactor/acceptor.c
index 952e16f..f7202d4 100644
--- a/proton-c/src/reactor/acceptor.c
+++ b/proton-c/src/reactor/acceptor.c
@@ -39,10 +39,6 @@ void pni_acceptor_readable(pn_selectable_t *sel) {
   pn_connection_t *conn = pn_reactor_connection(reactor, handler);
   pn_transport_t *trans = pn_transport();
   pn_transport_set_server(trans);
-  pn_sasl_t *sasl = pn_sasl(trans);
-  pn_sasl_allow_skip(sasl, true);
-  pn_sasl_mechanisms(sasl, "ANONYMOUS");
-  pn_sasl_done(sasl, PN_SASL_OK);
   pn_transport_bind(trans, conn);
   pn_decref(trans);
   pn_reactor_selectable_transport(reactor, sock, trans);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/reactor/connection.c
----------------------------------------------------------------------
diff --git a/proton-c/src/reactor/connection.c b/proton-c/src/reactor/connection.c
index 3560fc5..a6e7c5a 100644
--- a/proton-c/src/reactor/connection.c
+++ b/proton-c/src/reactor/connection.c
@@ -101,8 +101,6 @@ void pni_handle_open(pn_reactor_t *reactor, pn_event_t *event) {
   }
 
   pn_transport_t *transport = pn_transport();
-  pn_sasl_t *sasl = pn_sasl(transport);
-  pn_sasl_mechanisms(sasl, "ANONYMOUS");
   pn_transport_bind(transport, conn);
   pn_decref(transport);
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/sasl/cyrus_sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/cyrus_sasl.c b/proton-c/src/sasl/cyrus_sasl.c
new file mode 100644
index 0000000..9e26517
--- /dev/null
+++ b/proton-c/src/sasl/cyrus_sasl.c
@@ -0,0 +1,798 @@
+/*
+ *
+ * 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 "sasl-internal.h"
+
+#include "protocol.h"
+#include "dispatch_actions.h"
+#include "engine/engine-internal.h"
+#include "proton/codec.h"
+
+#include <sasl/sasl.h>
+
+enum pni_sasl_state {
+  SASL_NONE,
+  SASL_POSTED_INIT,
+  SASL_POSTED_MECHANISMS,
+  SASL_POSTED_RESPONSE,
+  SASL_POSTED_CHALLENGE,
+  SASL_PRETEND_OUTCOME,
+  SASL_RECVED_OUTCOME,
+  SASL_POSTED_OUTCOME
+};
+
+struct pni_sasl_t {
+    // Client selected mechanism
+    char *selected_mechanism;
+    char *included_mechanisms;
+    const char *username;
+    char *password;
+    const char *config_name;
+    char *config_dir;
+    const char *remote_fqdn;
+    sasl_conn_t *cyrus_conn;
+    pn_sasl_outcome_t outcome;
+    pn_bytes_t cyrus_out;
+    enum pni_sasl_state desired_state;
+    enum pni_sasl_state last_state;
+    bool client;
+    bool halt;
+};
+
+static bool pni_sasl_is_server_state(enum pni_sasl_state state)
+{
+  return state==SASL_NONE
+      || state==SASL_POSTED_MECHANISMS
+      || state==SASL_POSTED_CHALLENGE
+      || state==SASL_POSTED_OUTCOME;
+}
+
+static bool pni_sasl_is_client_state(enum pni_sasl_state state)
+{
+  return state==SASL_NONE
+      || state==SASL_POSTED_INIT
+      || state==SASL_POSTED_RESPONSE
+      || state==SASL_PRETEND_OUTCOME
+      || state==SASL_RECVED_OUTCOME;
+}
+
+static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl)
+{
+  enum pni_sasl_state last_state = sasl->last_state;
+  enum pni_sasl_state desired_state = sasl->desired_state;
+  return last_state==SASL_RECVED_OUTCOME
+      || desired_state==SASL_POSTED_OUTCOME;
+}
+
+static bool pni_sasl_is_final_output_state(pni_sasl_t *sasl)
+{
+  enum pni_sasl_state last_state = sasl->last_state;
+  return last_state==SASL_PRETEND_OUTCOME
+      || last_state==SASL_RECVED_OUTCOME
+      || last_state==SASL_POSTED_OUTCOME;
+}
+
+static const char *amqp_service = "amqp";
+
+static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl)
+{
+    // The external pn_sasl_t is really a pointer to the internal pni_transport_t
+    return ((pn_transport_t *)sasl);
+}
+
+static inline pni_sasl_t *get_sasl_internal(pn_sasl_t *sasl)
+{
+    // The external pn_sasl_t is really a pointer to the internal pni_transport_t
+    return sasl ? ((pn_transport_t *)sasl)->sasl : NULL;
+}
+
+static void pni_emit(pn_transport_t *transport)
+{
+  if (transport->connection && transport->connection->collector) {
+    pn_collector_t *collector = transport->connection->collector;
+    pn_collector_put(collector, PN_OBJECT, transport, PN_TRANSPORT);
+  }
+}
+
+// Look for symbol in the mech include list - not particlarly efficient,
+// but probably not used enough to matter.
+//
+// Note that if there is no inclusion list then every mech is implicitly included.
+static bool pni_included_mech(const char *included_mech_list, pn_bytes_t s)
+{
+  if (!included_mech_list) return true;
+
+  const char * end_list = included_mech_list+strlen(included_mech_list);
+  size_t len = s.size;
+  const char *c = included_mech_list;
+  while (c!=NULL) {
+    // If there are not enough chars left in the list no matches
+    if ((ptrdiff_t)len > end_list-c) return false;
+
+    // Is word equal with a space or end of string afterwards?
+    if (strncasecmp(c, s.start, len)==0 && (c[len]==' ' || c[len]==0) ) return true;
+
+    c = strchr(c, ' ');
+    c = c ? c+1 : NULL;
+  }
+  return false;
+}
+
+// This takes a space separated list and zero terminates it in place
+// whilst adding pointers to the existing strings in a string array.
+// This means that you can't free the original storage until you have
+// finished with the resulting list.
+static void pni_split_mechs(char *mechlist, const char* included_mechs, char *mechs[], int *count)
+{
+  char *start = mechlist;
+  char *end = start;
+
+  while (*end) {
+    if (*end == ' ') {
+      if (start != end) {
+        *end = '\0';
+        if (pni_included_mech(included_mechs, pn_bytes(end-start, start))) {
+          mechs[(*count)++] = start;
+        }
+      }
+      end++;
+      start = end;
+    } else {
+      end++;
+    }
+  }
+
+  if (start != end) {
+    if (pni_included_mech(included_mechs, pn_bytes(end-start, start))) {
+      mechs[(*count)++] = start;
+    }
+  }
+}
+
+static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logger)
+{
+    if (r!=SASL_OK) {
+        pn_transport_logf(logger, "sasl error: %s", conn ? sasl_errdetail(conn) : sasl_errstring(r, NULL, NULL));
+        return false;
+    }
+    return true;
+}
+
+// Cyrus wrappers
+static void pni_cyrus_interact(pni_sasl_t *sasl, sasl_interact_t *interact)
+{
+  for (sasl_interact_t *i = interact; i->id!=SASL_CB_LIST_END; i++) {
+    switch (i->id) {
+    case SASL_CB_USER:
+      i->result = sasl->username;
+      i->len = strlen(sasl->username);
+      break;
+    case SASL_CB_AUTHNAME:
+      i->result = sasl->username;
+      i->len = strlen(sasl->username);
+      break;
+    case SASL_CB_PASS:
+      i->result = sasl->password;
+      i->len = strlen(sasl->password);
+      break;
+    default:
+      fprintf(stderr, "(%s): %s - %s\n", i->challenge, i->prompt, i->defresult);
+    }
+  }
+}
+
+static void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state desired_state)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  if (sasl->last_state > desired_state) {
+    pn_transport_logf(transport, "Trying to send SASL frame (%d), but illegal: already in later state (%d)", desired_state, sasl->last_state);
+  } else if (sasl->client && !pni_sasl_is_client_state(desired_state)) {
+    pn_transport_logf(transport, "Trying to send server SASL frame (%d) on a client", desired_state);
+  } else if (!sasl->client && !pni_sasl_is_server_state(desired_state)) {
+    pn_transport_logf(transport, "Trying to send client SASL frame (%d) on a server", desired_state);
+  } else {
+    // If we need to repeat CHALLENGE or RESPONSE frames adjust current state to seem
+    // like they haven't been sent yet
+    if (sasl->last_state==desired_state && desired_state==SASL_POSTED_RESPONSE) {
+      sasl->last_state = SASL_POSTED_INIT;
+    }
+    if (sasl->last_state==desired_state && desired_state==SASL_POSTED_CHALLENGE) {
+      sasl->last_state = SASL_POSTED_MECHANISMS;
+    }
+    sasl->desired_state = desired_state;
+  }
+}
+
+// Post SASL frame
+static void pni_post_sasl_frame(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  pn_bytes_t out = sasl->cyrus_out;
+  enum pni_sasl_state desired_state = sasl->desired_state;
+  while (sasl->desired_state > sasl->last_state) {
+    switch (desired_state) {
+    case SASL_POSTED_INIT:
+      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->selected_mechanism,
+                    out.size, out.start);
+      break;
+    case SASL_PRETEND_OUTCOME:
+      if (sasl->last_state < SASL_POSTED_INIT) {
+        desired_state = SASL_POSTED_INIT;
+        continue;
+      }
+      break;
+    case SASL_POSTED_MECHANISMS: {
+      // TODO: Hardcoded limit of 16 mechanisms
+      char *mechs[16];
+      int count = 0;
+
+      char *mechlist = NULL;
+      if (sasl->cyrus_conn) {
+        const char *result = NULL;
+
+        int r = sasl_listmech(sasl->cyrus_conn, NULL, "", " ", "", &result, NULL, NULL);
+        if (pni_check_sasl_result(sasl->cyrus_conn, r, transport)) {
+          if (result && *result) {
+            mechlist = strdup(result);
+            pni_split_mechs(mechlist, sasl->included_mechanisms, mechs, &count);
+          }
+        }
+      }
+      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs);
+      free(mechlist);
+      break;
+    }
+    case SASL_POSTED_RESPONSE:
+      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_RESPONSE, out.size, out.start);
+      break;
+    case SASL_POSTED_CHALLENGE:
+      if (sasl->last_state < SASL_POSTED_MECHANISMS) {
+        desired_state = SASL_POSTED_MECHANISMS;
+        continue;
+      }
+      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_CHALLENGE, out.size, out.start);
+      break;
+    case SASL_POSTED_OUTCOME:
+      if (sasl->last_state < SASL_POSTED_MECHANISMS) {
+        desired_state = SASL_POSTED_MECHANISMS;
+        continue;
+      }
+      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome);
+      break;
+    case SASL_NONE:
+    case SASL_RECVED_OUTCOME:
+      return;
+    }
+    sasl->last_state = desired_state;
+    desired_state = sasl->desired_state;
+  }
+  pni_emit(transport);
+}
+
+// Set up callbacks to use interact
+static const sasl_callback_t pni_user_password_callbacks[] = {
+  {SASL_CB_USER, NULL, NULL},
+  {SASL_CB_AUTHNAME, NULL, NULL},
+  {SASL_CB_PASS, NULL, NULL},
+  {SASL_CB_LIST_END, NULL, NULL},
+};
+
+static const sasl_callback_t pni_user_callbacks[] = {
+  {SASL_CB_USER, NULL, NULL},
+  {SASL_CB_AUTHNAME, NULL, NULL},
+  {SASL_CB_LIST_END, NULL, NULL},
+};
+
+static int pni_wrap_client_start(pni_sasl_t *sasl, const char *mechs, const char **mechusing)
+{
+  int result;
+
+  if (sasl->config_dir) {
+    result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir);
+    if (result!=SASL_OK) return result;
+  }
+
+  result = sasl_client_init(NULL);
+  if (result!=SASL_OK) return result;
+
+  const sasl_callback_t *callbacks = sasl->username ? sasl->password ? pni_user_password_callbacks : pni_user_callbacks : NULL;
+  result = sasl_client_new(amqp_service,
+                           sasl->remote_fqdn,
+                           NULL, NULL,
+                           callbacks, 0,
+                           &sasl->cyrus_conn);
+  if (result!=SASL_OK) return result;
+
+  sasl_interact_t *client_interact=NULL;
+  const char *out;
+  unsigned outlen;
+
+  do {
+
+    result = sasl_client_start(sasl->cyrus_conn,
+                               mechs,
+                               &client_interact,
+                               &out, &outlen,
+                               mechusing);
+    if (result==SASL_INTERACT) {
+      pni_cyrus_interact(sasl, client_interact);
+    }
+  } while (result==SASL_INTERACT);
+
+  sasl->cyrus_out.start = out;
+  sasl->cyrus_out.size = outlen;
+  return result;
+}
+
+static void pni_process_mechanisms(pn_transport_t *transport, const char *mechs, bool short_circuit)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  const char *mech_selected;
+  int result = pni_wrap_client_start(sasl, mechs, &mech_selected);
+  switch (result) {
+  case SASL_OK:
+  case SASL_CONTINUE:
+    sasl->selected_mechanism = strdup(mech_selected);
+    pni_sasl_set_desired_state(transport, short_circuit ? SASL_PRETEND_OUTCOME : SASL_POSTED_INIT);
+    break;
+  case SASL_NOMECH:
+  default:
+    pni_check_sasl_result(sasl->cyrus_conn, result, transport);
+    sasl->last_state = SASL_RECVED_OUTCOME;
+    sasl->halt = true;
+    pn_transport_close_tail(transport);
+    break;
+  }
+}
+
+
+static int pni_wrap_client_step(pni_sasl_t *sasl, const pn_bytes_t *in)
+{
+  sasl_interact_t *client_interact=NULL;
+  const char *out;
+  unsigned outlen;
+
+  int result;
+  do {
+
+    result = sasl_client_step(sasl->cyrus_conn,
+                              in->start, in->size,
+                              &client_interact,
+                              &out, &outlen);
+    if (result==SASL_INTERACT) {
+      pni_cyrus_interact(sasl, client_interact);
+    }
+  } while (result==SASL_INTERACT);
+
+  sasl->cyrus_out.start = out;
+  sasl->cyrus_out.size = outlen;
+  return result;
+}
+
+static void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  int result = pni_wrap_client_step(sasl, recv);
+  switch (result) {
+  case SASL_OK:
+    // Authenticated
+    // TODO: Documented that we need to call sasl_client_step() again to be sure!;
+  case SASL_CONTINUE:
+    // Need to send a response
+    pni_sasl_set_desired_state(transport, SASL_POSTED_RESPONSE);
+    break;
+  default:
+    pni_check_sasl_result(sasl->cyrus_conn, result, transport);
+
+    // Failed somehow
+    sasl->halt = true;
+    pn_transport_close_tail(transport);
+    break;
+  }
+}
+
+static int pni_wrap_server_new(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  int result;
+
+  if (sasl->config_dir) {
+    result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir);
+    if (result!=SASL_OK) return result;
+  }
+
+  result = sasl_server_init(NULL, sasl->config_name);
+  if (result!=SASL_OK) return result;
+
+  result = sasl_server_new(amqp_service, NULL, NULL, NULL, NULL, NULL, 0, &sasl->cyrus_conn);
+  if (result!=SASL_OK) return result;
+
+  sasl_security_properties_t secprops = {0};
+  secprops.security_flags =
+    SASL_SEC_NOPLAINTEXT |
+    ( transport->auth_required ? SASL_SEC_NOANONYMOUS : 0 ) ;
+
+  result = sasl_setprop(sasl->cyrus_conn, SASL_SEC_PROPS, &secprops);
+  if (result!=SASL_OK) return result;
+
+  // EXTERNAL not implemented yet
+#if 0
+  sasl_ssf_t ssf = 128;
+  result = sasl_setprop(sasl->cyrus_conn, SASL_SSF_EXTERNAL, &ssf);
+  if (result!=SASL_OK) return result;
+
+  const char *extid = "user";
+  result = sasl_setprop(sasl->cyrus_conn, SASL_AUTH_EXTERNAL, extid);
+  if (result!=SASL_OK) return result;
+#endif
+
+  return result;
+}
+
+static int pni_wrap_server_start(pni_sasl_t *sasl, const char *mech_selected, const pn_bytes_t *in)
+{
+  int result;
+  const char *out;
+  unsigned outlen;
+  result = sasl_server_start(sasl->cyrus_conn,
+                             mech_selected,
+                             in->start, in->size,
+                             &out, &outlen);
+
+  sasl->cyrus_out.start = out;
+  sasl->cyrus_out.size = outlen;
+  return result;
+}
+
+static void pni_process_server_result(pn_transport_t *transport, int result)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  switch (result) {
+  case SASL_OK:
+    // Authenticated
+    sasl->outcome = PN_SASL_OK;
+    transport->authenticated = true;
+    // Get username from SASL
+    const void* value;
+    sasl_getprop(sasl->cyrus_conn, SASL_USERNAME, &value);
+    sasl->username = (const char*) value;
+    pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism);
+    pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+    break;
+  case SASL_CONTINUE:
+    // Need to send a challenge
+    pni_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE);
+    break;
+  default:
+    pni_check_sasl_result(sasl->cyrus_conn, result, transport);
+
+    // Failed to authenticate
+    sasl->outcome = PN_SASL_AUTH;
+    pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+    break;
+  }
+  pni_emit(transport);
+}
+
+static void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
+{
+  pni_sasl_t *sasl = transport->sasl;
+
+  int result = pni_wrap_server_start(sasl, mechanism, recv);
+  if (result==SASL_OK) {
+    // We need to filter out a supplied mech in in the inclusion list
+    // as the client could have used a mech that we support, but that
+    // wasn't on the list we sent.
+    if (!pni_included_mech(sasl->included_mechanisms, pn_bytes(strlen(mechanism), mechanism))) {
+      sasl_seterror(sasl->cyrus_conn, 0, "Client mechanism not in mechanism inclusion list.");
+      result = SASL_FAIL;
+    }
+  }
+  pni_process_server_result(transport, result);
+}
+
+static int pni_wrap_server_step(pni_sasl_t *sasl, const pn_bytes_t *in)
+{
+    int result;
+    const char *out;
+    unsigned outlen;
+    result = sasl_server_step(sasl->cyrus_conn,
+                              in->start, in->size,
+                              &out, &outlen);
+
+    sasl->cyrus_out.start = out;
+    sasl->cyrus_out.size = outlen;
+    return result;
+}
+
+static void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  int result = pni_wrap_server_step(sasl, recv);
+  pni_process_server_result(transport, result);
+}
+
+pn_sasl_t *pn_sasl(pn_transport_t *transport)
+{
+  if (!transport->sasl) {
+    pni_sasl_t *sasl = (pni_sasl_t *) malloc(sizeof(pni_sasl_t));
+
+    const char *sasl_config_path = getenv("PN_SASL_CONFIG_PATH");
+
+    sasl->client = !transport->server;
+    sasl->selected_mechanism = NULL;
+    sasl->included_mechanisms = NULL;
+    sasl->username = NULL;
+    sasl->password = NULL;
+    sasl->config_name = sasl->client ? "proton-client" : "proton-server";
+    sasl->config_dir =  sasl_config_path ? strdup(sasl_config_path) : NULL;
+    sasl->remote_fqdn = NULL;
+    sasl->outcome = PN_SASL_NONE;
+    sasl->cyrus_conn = NULL;
+    sasl->cyrus_out.size = 0;
+    sasl->cyrus_out.start = NULL;
+    sasl->desired_state = SASL_NONE;
+    sasl->last_state = SASL_NONE;
+    sasl->halt = false;
+
+    transport->sasl = sasl;
+  }
+
+  // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t
+  return (pn_sasl_t *)transport;
+}
+
+// This is a hack to tell us that
+// no actual negotiation is going to happen and we can go
+// straight to the AMQP layer; it can only work on the client side
+// As the server doesn't know if SASL is even active until it sees
+// the SASL header from the client first.
+static void pni_sasl_force_anonymous(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  if (sasl->client) {
+    // Pretend we got sasl mechanisms frame with just ANONYMOUS
+    pni_process_mechanisms(transport, "ANONYMOUS", true);
+  }
+  pni_emit(transport);
+}
+
+void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  sasl->remote_fqdn = fqdn;
+}
+
+void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  sasl->username = user;
+  free(sasl->password);
+  sasl->password = password ? strdup(password) : NULL;
+}
+
+const char *pn_sasl_get_user(pn_sasl_t *sasl0)
+{
+    pni_sasl_t *sasl = get_sasl_internal(sasl0);
+    return sasl->username;
+}
+
+const char *pn_sasl_get_mech(pn_sasl_t *sasl0)
+{
+    pni_sasl_t *sasl = get_sasl_internal(sasl0);
+    return sasl->selected_mechanism;
+}
+
+void pn_sasl_allowed_mechs(pn_sasl_t *sasl0, const char *mechs)
+{
+    pni_sasl_t *sasl = get_sasl_internal(sasl0);
+    free(sasl->included_mechanisms);
+    sasl->included_mechanisms = mechs ? strdup(mechs) : NULL;
+    if (strcmp(mechs, "ANONYMOUS")==0 ) {
+      pn_transport_t *transport = get_transport_internal(sasl0);
+      pni_sasl_force_anonymous(transport);
+    }
+}
+
+void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name)
+{
+    pni_sasl_t *sasl = get_sasl_internal(sasl0);
+    sasl->config_name = name;
+}
+
+void pn_sasl_config_path(pn_sasl_t *sasl0, const char *dir)
+{
+    pni_sasl_t *sasl = get_sasl_internal(sasl0);
+    free(sasl->config_dir);
+    sasl->config_dir = strdup(dir);
+}
+
+void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (sasl) {
+    sasl->outcome = outcome;
+  }
+}
+
+pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  return sasl ? sasl->outcome : PN_SASL_NONE;
+}
+
+void pn_sasl_free(pn_transport_t *transport)
+{
+  if (transport) {
+    pni_sasl_t *sasl = transport->sasl;
+    if (sasl) {
+      free(sasl->selected_mechanism);
+      free(sasl->included_mechanisms);
+      free(sasl->password);
+      free(sasl->config_dir);
+
+      // CYRUS_SASL
+      if (sasl->cyrus_conn) {
+          sasl_dispose(&sasl->cyrus_conn);
+          if (sasl->client) {
+              sasl_client_done();
+          } else {
+              sasl_server_done();
+          }
+      }
+
+      free(sasl);
+    }
+  }
+}
+
+static void pni_sasl_server_init(pn_transport_t *transport)
+{
+  int r = pni_wrap_server_new(transport);
+
+  if (!pni_check_sasl_result(transport->sasl->cyrus_conn, r, transport)) {
+      return;
+  }
+
+  // Setup to send SASL mechanisms frame
+  pni_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS);
+}
+
+static void pn_sasl_process(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  if (!sasl->client) {
+    if (sasl->desired_state<SASL_POSTED_MECHANISMS) {
+      pni_sasl_server_init(transport);
+    }
+  }
+}
+
+ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available)
+{
+  pn_sasl_process(transport);
+
+  pni_sasl_t *sasl = transport->sasl;
+  ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt);
+
+  if (n==0 && pni_sasl_is_final_input_state(sasl)) {
+    return PN_EOS;
+  }
+  return n;
+}
+
+ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size)
+{
+  pn_sasl_process(transport);
+
+  pni_post_sasl_frame(transport);
+
+  pni_sasl_t *sasl = transport->sasl;
+  if (transport->available == 0 && pni_sasl_is_final_output_state(sasl)) {
+    if (sasl->outcome != PN_SASL_OK && pni_sasl_is_final_input_state(sasl)) {
+      pn_transport_close_tail(transport);
+    }
+    return PN_EOS;
+  } else {
+    return pn_dispatcher_output(transport, bytes, size);
+  }
+}
+
+// Received Server side
+int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  pn_bytes_t mech;
+  pn_bytes_t recv;
+  int err = pn_data_scan(args, "D.[sz]", &mech, &recv);
+  if (err) return err;
+  sasl->selected_mechanism = pn_strndup(mech.start, mech.size);
+
+  pni_process_init(transport, sasl->selected_mechanism, &recv);
+
+  return 0;
+}
+
+// Received client side
+int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  // If we already pretended we got the ANONYMOUS mech then ignore
+  if (transport->sasl->last_state==SASL_PRETEND_OUTCOME) return 0;
+
+  pn_string_t *mechs = pn_string("");
+
+  // This scanning relies on pn_data_scan leaving the pn_data_t cursors
+  // where they are after finishing the scan
+  int err = pn_data_scan(args, "D.[@[");
+  if (err) return err;
+
+  // Now keep checking for end of array and pull a symbol
+  while(pn_data_next(args)) {
+    pn_bytes_t s = pn_data_get_symbol(args);
+    if (pni_included_mech(transport->sasl->included_mechanisms, s)) {
+      pn_string_addf(mechs, "%*s ", (int)s.size, s.start);
+    }
+  }
+  pn_string_buffer(mechs)[pn_string_size(mechs)-1] = 0;
+
+  pni_process_mechanisms(transport, pn_string_get(mechs), false);
+
+  pn_free(mechs);
+  return 0;
+}
+
+// Received client side
+int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pn_bytes_t recv;
+  int err = pn_data_scan(args, "D.[z]", &recv);
+  if (err) return err;
+
+  pni_process_challenge(transport, &recv);
+
+  return 0;
+}
+
+// Received server side
+int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pn_bytes_t recv;
+  int err = pn_data_scan(args, "D.[z]", &recv);
+  if (err) return err;
+
+  pni_process_response(transport, &recv);
+
+  return 0;
+}
+
+// Received client side
+int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  uint8_t outcome;
+  int err = pn_data_scan(args, "D.[B]", &outcome);
+  if (err) return err;
+  sasl->outcome = (pn_sasl_outcome_t) outcome;
+  sasl->last_state = SASL_RECVED_OUTCOME;
+  sasl->halt = true;
+  pni_emit(transport);
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/sasl/none_sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/none_sasl.c b/proton-c/src/sasl/none_sasl.c
new file mode 100644
index 0000000..87f7917
--- /dev/null
+++ b/proton-c/src/sasl/none_sasl.c
@@ -0,0 +1,425 @@
+/*
+ *
+ * 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 <assert.h>
+#include <proton/codec.h>
+
+#include "buffer.h"
+#include "protocol.h"
+#include "dispatch_actions.h"
+#include "engine/engine-internal.h"
+
+struct pni_sasl_t {
+  char *selected_mechanism;
+  char *included_mechanisms;
+  const char *username;
+  char *password;
+  const char *remote_fqdn;
+  pn_buffer_t *send_data;
+  pn_buffer_t *recv_data;
+  pn_sasl_outcome_t outcome;
+  bool client;
+  bool sent_init;
+  bool rcvd_init;
+  bool sent_done;
+  bool rcvd_done;
+  bool halt;
+};
+
+static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl)
+{
+    // The external pn_sasl_t is really a pointer to the internal pni_transport_t
+    return ((pn_transport_t *)sasl);
+}
+
+static inline pni_sasl_t *get_sasl_internal(pn_sasl_t *sasl)
+{
+    // The external pn_sasl_t is really a pointer to the internal pni_transport_t
+    return sasl ? ((pn_transport_t *)sasl)->sasl : NULL;
+}
+
+static void pni_emit(pn_sasl_t *sasl) {
+  pn_transport_t *transport = get_transport_internal(sasl);
+  if (transport->connection && transport->connection->collector) {
+    pn_collector_t *collector = transport->connection->collector;
+    pn_collector_put(collector, PN_OBJECT, transport, PN_TRANSPORT);
+  }
+}
+
+pn_sasl_t *pn_sasl(pn_transport_t *transport)
+{
+  if (!transport->sasl) {
+    pni_sasl_t *sasl = (pni_sasl_t *) malloc(sizeof(pni_sasl_t));
+
+    sasl->client = !transport->server;
+    sasl->included_mechanisms = NULL;
+    sasl->selected_mechanism = NULL;
+    sasl->send_data = pn_buffer(16);
+    sasl->recv_data = pn_buffer(16);
+    sasl->outcome = PN_SASL_NONE;
+    sasl->sent_init = false;
+    sasl->rcvd_init = false;
+    sasl->sent_done = false;
+    sasl->rcvd_done = false;
+    sasl->halt = false;
+
+    transport->sasl = sasl;
+  }
+
+  // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t
+  return (pn_sasl_t *)transport;
+}
+
+void pn_sasl_allowed_mechs(pn_sasl_t *sasl0, const char *mechs)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (!sasl) return;
+  free(sasl->included_mechanisms);
+  sasl->included_mechanisms = mechs ? strdup(mechs) : NULL;
+  if (strcmp(mechs, "ANONYMOUS")==0 ) {
+    // If we do this on the client it is a hack to tell us that
+    // no actual negatiation is going to happen and we can go
+    // straight to the AMQP layer
+    if (sasl->client) {
+        sasl->rcvd_done = true;
+        sasl->sent_done = true;
+    }
+  }
+  pni_emit(sasl0);
+}
+
+ssize_t pn_sasl_send(pn_sasl_t *sasl0, const char *bytes, size_t size)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (sasl) {
+    if (pn_buffer_size(sasl->send_data)) {
+      // XXX: need better error
+      return PN_STATE_ERR;
+    }
+    int err = pn_buffer_append(sasl->send_data, bytes, size);
+    if (err) return err;
+    pni_emit(sasl0);
+    return size;
+  } else {
+    return PN_ARG_ERR;
+  }
+}
+
+size_t pn_sasl_pending(pn_sasl_t *sasl0)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (sasl && pn_buffer_size(sasl->recv_data)) {
+    return pn_buffer_size(sasl->recv_data);
+  } else {
+    return 0;
+  }
+}
+
+ssize_t pn_sasl_recv(pn_sasl_t *sasl0, char *bytes, size_t size)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (!sasl) return PN_ARG_ERR;
+
+  size_t bsize = pn_buffer_size(sasl->recv_data);
+  if (bsize) {
+    if (bsize > size) return PN_OVERFLOW;
+    pn_buffer_get(sasl->recv_data, 0, bsize, bytes);
+    pn_buffer_clear(sasl->recv_data);
+    return bsize;
+  } else {
+    return PN_EOS;
+  }
+}
+
+void pn_sasl_client(pn_sasl_t *sasl)
+{
+}
+
+void pn_sasl_server(pn_sasl_t *sasl0)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (sasl) {
+    sasl->client = false;
+  }
+}
+
+void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  sasl->username = user;
+  sasl->password = password ? strdup(password) : NULL;
+}
+
+const char *pn_sasl_get_user(pn_sasl_t *sasl0)
+{
+  return "anonymous";
+}
+
+const char *pn_sasl_get_mech(pn_sasl_t *sasl0)
+{
+  return "ANONYMOUS";
+}
+
+void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name)
+{
+}
+
+void pn_sasl_config_path(pn_sasl_t *sasl0, const char *path)
+{
+}
+
+void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  sasl->remote_fqdn = fqdn;
+}
+
+void pn_sasl_plain(pn_sasl_t *sasl0, const char *username, const char *password)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (!sasl) return;
+
+  const char *user = username ? username : "";
+  const char *pass = password ? password : "";
+  size_t usize = strlen(user);
+  size_t psize = strlen(pass);
+  size_t size = usize + psize + 2;
+  char *iresp = (char *) malloc(size);
+
+  iresp[0] = 0;
+  memmove(iresp + 1, user, usize);
+  iresp[usize + 1] = 0;
+  memmove(iresp + usize + 2, pass, psize);
+
+  pn_sasl_allowed_mechs(sasl0, "PLAIN");
+  pn_sasl_send(sasl0, iresp, size);
+  free(iresp);
+}
+
+void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  if (sasl) {
+    sasl->outcome = outcome;
+    pni_emit(sasl0);
+  }
+}
+
+pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0)
+{
+  pni_sasl_t *sasl = get_sasl_internal(sasl0);
+  return sasl ? sasl->outcome : PN_SASL_NONE;
+}
+
+void pn_sasl_free(pn_transport_t *transport)
+{
+  if (transport) {
+    pni_sasl_t *sasl = transport->sasl;
+    if (sasl) {
+      free(sasl->included_mechanisms);
+      free(sasl->selected_mechanism);
+      pn_buffer_free(sasl->send_data);
+      pn_buffer_free(sasl->recv_data);
+      free(sasl);
+    }
+  }
+}
+
+void pn_client_init(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data);
+  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->included_mechanisms,
+                bytes.size, bytes.start);
+  pn_buffer_clear(sasl->send_data);
+  pni_emit((pn_sasl_t *) transport);
+}
+
+void pni_sasl_server_init(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  // XXX
+  char *mechs[16];
+  int count = 0;
+
+  if (sasl->included_mechanisms) {
+    char *start = sasl->included_mechanisms;
+    char *end = start;
+
+    while (*end) {
+      if (*end == ' ') {
+        if (start != end) {
+          *end = '\0';
+          mechs[count++] = start;
+        }
+        end++;
+        start = end;
+      } else {
+        end++;
+      }
+    }
+
+    if (start != end) {
+      mechs[count++] = start;
+    }
+  }
+
+  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs);
+  pni_emit((pn_sasl_t *) transport);
+}
+
+void pn_server_done(pn_sasl_t *sasl0)
+{
+  pn_transport_t *transport = get_transport_internal(sasl0);
+  pni_sasl_t *sasl = transport->sasl;
+  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome);
+  pni_emit(sasl0);
+}
+
+void pn_sasl_process(pn_transport_t *transport)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  if (!sasl->sent_init) {
+    if (sasl->client) {
+      pn_client_init(transport);
+    } else {
+      pni_sasl_server_init(transport);
+    }
+    sasl->sent_init = true;
+  }
+
+  if (pn_buffer_size(sasl->send_data)) {
+    pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data);
+    pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", sasl->client ? SASL_RESPONSE : SASL_CHALLENGE,
+                  bytes.size, bytes.start);
+    pn_buffer_clear(sasl->send_data);
+    pni_emit((pn_sasl_t *) transport);
+  }
+
+  if (!sasl->client && sasl->outcome != PN_SASL_NONE && !sasl->sent_done) {
+    pn_server_done((pn_sasl_t *)transport);
+    sasl->sent_done = true;
+  }
+
+  // XXX: need to finish this check when challenge/response is complete
+  //      check for client is outome is received
+  //      check for server is that there are no pending frames (either init
+  //      or challenges) from client
+  if (!sasl->client && sasl->sent_done && sasl->rcvd_init) {
+    sasl->rcvd_done = true;
+    sasl->halt = true;
+  }
+}
+
+ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt);
+  if (n < 0) return n;
+
+  pn_sasl_process(transport);
+
+  if (sasl->rcvd_done) {
+    if (sasl->outcome == PN_SASL_OK) {
+      if (n) {
+        return n;
+      } else {
+        return PN_EOS;
+      }
+    } else {
+      // XXX: should probably do something better here
+      return PN_EOS;
+    }
+  } else {
+    return n;
+  }
+}
+
+ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size)
+{
+  pn_sasl_process(transport);
+
+  pni_sasl_t *sasl = transport->sasl;
+  if (transport->available == 0 && sasl->sent_done) {
+    if (sasl->outcome == PN_SASL_OK) {
+      return PN_EOS;
+    } else {
+      // XXX: should probably do something better here
+      return PN_EOS;
+    }
+  } else {
+    return pn_dispatcher_output(transport, bytes, size);
+  }
+}
+
+int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  pn_bytes_t mech;
+  pn_bytes_t recv;
+  int err = pn_data_scan(args, "D.[sz]", &mech, &recv);
+  if (err) return err;
+  sasl->selected_mechanism = pn_strndup(mech.start, mech.size);
+  pn_buffer_append(sasl->recv_data, recv.start, recv.size);
+  sasl->rcvd_init = true;
+  return 0;
+}
+
+int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  sasl->rcvd_init = true;
+  return 0;
+}
+
+int pn_do_recv(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  pn_bytes_t recv;
+  int err = pn_data_scan(args, "D.[z]", &recv);
+  if (err) return err;
+  pn_buffer_append(sasl->recv_data, recv.start, recv.size);
+  return 0;
+}
+
+int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  return pn_do_recv(transport, frame_type, channel, args, payload);
+}
+
+int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  return pn_do_recv(transport, frame_type, channel, args, payload);
+}
+
+int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
+{
+  pni_sasl_t *sasl = transport->sasl;
+  uint8_t outcome;
+  int err = pn_data_scan(args, "D.[B]", &outcome);
+  if (err) return err;
+  sasl->outcome = (pn_sasl_outcome_t) outcome;
+  sasl->rcvd_done = true;
+  sasl->sent_done = true;
+  sasl->halt = true;
+  pni_emit((pn_sasl_t *) transport);
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/sasl/sasl-internal.h
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl-internal.h b/proton-c/src/sasl/sasl-internal.h
index a8390df..965eff2 100644
--- a/proton-c/src/sasl/sasl-internal.h
+++ b/proton-c/src/sasl/sasl-internal.h
@@ -22,7 +22,7 @@
 #ifndef PROTON_SASL_INTERNAL_H
 #define PROTON_SASL_INTERNAL_H 1
 
-#include <proton/sasl.h>
+#include "proton/types.h"
 
 /** Destructor for the given SASL layer.
  *
@@ -31,6 +31,10 @@
  */
 void pn_sasl_free(pn_transport_t *transport);
 
-bool pn_sasl_skipping_allowed(pn_transport_t *transport);
+ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available);
+ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size);
+
+void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password);
+void pni_sasl_set_remote_hostname(pn_transport_t *transport, const char* fqdn);
 
 #endif /* sasl-internal.h */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/sasl/sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl.c b/proton-c/src/sasl/sasl.c
index 191c23e..0c58b1f 100644
--- a/proton-c/src/sasl/sasl.c
+++ b/proton-c/src/sasl/sasl.c
@@ -19,39 +19,12 @@
  *
  */
 
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <proton/error.h>
-#include <proton/sasl.h>
+#include "sasl-internal.h"
 
-#include "buffer.h"
-#include "protocol.h"
-#include "dispatch_actions.h"
-#include "framing/framing.h"
 #include "engine/engine-internal.h"
-#include "dispatcher/dispatcher.h"
-#include "util.h"
 #include "transport/autodetect.h"
 
-
-struct pni_sasl_t {
-  char *mechanisms;
-  char *remote_mechanisms;
-  pn_buffer_t *send_data;
-  pn_buffer_t *recv_data;
-  pn_sasl_outcome_t outcome;
-  bool client;
-  bool allow_skip;
-  bool sent_init;
-  bool rcvd_init;
-  bool sent_done;
-  bool rcvd_done;
-  bool halt;
-  bool input_bypass;
-  bool output_bypass;
-};
+#include <assert.h>
 
 static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl)
 {
@@ -59,10 +32,11 @@ static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl)
     return ((pn_transport_t *)sasl);
 }
 
-static inline pni_sasl_t *get_sasl_internal(pn_sasl_t *sasl)
+void pn_sasl_allow_skip(pn_sasl_t *sasl0, bool allow)
 {
-    // The external pn_sasl_t is really a pointer to the internal pni_transport_t
-    return sasl ? ((pn_transport_t *)sasl)->sasl : NULL;
+    if (!sasl0) return;
+    pn_transport_t *transport = get_transport_internal(sasl0);
+    pn_transport_require_auth(transport, !allow);
 }
 
 static ssize_t pn_input_read_sasl_header(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available);
@@ -98,389 +72,6 @@ const pn_io_layer_t sasl_layer = {
     NULL
 };
 
-static void pni_emit(pn_sasl_t *sasl) {
-  pn_transport_t *transport = get_transport_internal(sasl);
-  if (transport->connection && transport->connection->collector) {
-    pn_collector_t *collector = transport->connection->collector;
-    pn_collector_put(collector, PN_OBJECT, transport, PN_TRANSPORT);
-  }
-}
-
-pn_sasl_t *pn_sasl(pn_transport_t *transport)
-{
-  if (!transport->sasl) {
-    pni_sasl_t *sasl = (pni_sasl_t *) malloc(sizeof(pni_sasl_t));
-
-    sasl->client = !transport->server;
-    sasl->mechanisms = NULL;
-    sasl->remote_mechanisms = NULL;
-    sasl->send_data = pn_buffer(16);
-    sasl->recv_data = pn_buffer(16);
-    sasl->outcome = PN_SASL_NONE;
-    sasl->allow_skip = true;
-    sasl->sent_init = false;
-    sasl->rcvd_init = false;
-    sasl->sent_done = false;
-    sasl->rcvd_done = false;
-    sasl->input_bypass = false;
-    sasl->output_bypass = false;
-    sasl->halt = false;
-
-    transport->sasl = sasl;
-  }
-
-  // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t
-  return (pn_sasl_t *)transport;
-}
-
-pn_sasl_state_t pn_sasl_state(pn_sasl_t *sasl0)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl) {
-    if (sasl->outcome == PN_SASL_NONE) {
-      return sasl->rcvd_init ? PN_SASL_STEP : PN_SASL_IDLE;
-    } else {
-      return sasl->outcome == PN_SASL_OK ? PN_SASL_PASS : PN_SASL_FAIL;
-    }
-    //    if (sasl->rcvd_init && sasl->outcome == PN_SASL_NONE) return PN_SASL_STEP;
-    //if (sasl->outcome == PN_SASL_OK) return PN_SASL_PASS;
-    //else return PN_SASL_FAIL;
-  } else {
-    return PN_SASL_FAIL;
-  }
-}
-
-void pn_sasl_mechanisms(pn_sasl_t *sasl0, const char *mechanisms)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (!sasl) return;
-  sasl->mechanisms = pn_strdup(mechanisms);
-  pni_emit(sasl0);
-}
-
-const char *pn_sasl_remote_mechanisms(pn_sasl_t *sasl0)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  return sasl ? sasl->remote_mechanisms : NULL;
-}
-
-ssize_t pn_sasl_send(pn_sasl_t *sasl0, const char *bytes, size_t size)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl) {
-    if (pn_buffer_size(sasl->send_data)) {
-      // XXX: need better error
-      return PN_STATE_ERR;
-    }
-    int err = pn_buffer_append(sasl->send_data, bytes, size);
-    if (err) return err;
-    pni_emit(sasl0);
-    return size;
-  } else {
-    return PN_ARG_ERR;
-  }
-}
-
-size_t pn_sasl_pending(pn_sasl_t *sasl0)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl && pn_buffer_size(sasl->recv_data)) {
-    return pn_buffer_size(sasl->recv_data);
-  } else {
-    return 0;
-  }
-}
-
-ssize_t pn_sasl_recv(pn_sasl_t *sasl0, char *bytes, size_t size)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (!sasl) return PN_ARG_ERR;
-
-  size_t bsize = pn_buffer_size(sasl->recv_data);
-  if (bsize) {
-    if (bsize > size) return PN_OVERFLOW;
-    pn_buffer_get(sasl->recv_data, 0, bsize, bytes);
-    pn_buffer_clear(sasl->recv_data);
-    return bsize;
-  } else {
-    return PN_EOS;
-  }
-}
-
-void pn_sasl_client(pn_sasl_t *sasl)
-{
-}
-
-void pn_sasl_server(pn_sasl_t *sasl0)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl) {
-    sasl->client = false;
-  }
-}
-
-void pn_sasl_allow_skip(pn_sasl_t *sasl0, bool allow)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl)
-    sasl->allow_skip = allow;
-}
-
-bool pn_sasl_skipping_allowed(pn_transport_t *transport)
-{
-  return transport && ( !transport->sasl || transport->sasl->allow_skip );
-}
-
-void pn_sasl_plain(pn_sasl_t *sasl0, const char *username, const char *password)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (!sasl) return;
-
-  const char *user = username ? username : "";
-  const char *pass = password ? password : "";
-  size_t usize = strlen(user);
-  size_t psize = strlen(pass);
-  size_t size = usize + psize + 2;
-  char *iresp = (char *) malloc(size);
-
-  iresp[0] = 0;
-  memmove(iresp + 1, user, usize);
-  iresp[usize + 1] = 0;
-  memmove(iresp + usize + 2, pass, psize);
-
-  pn_sasl_mechanisms(sasl0, "PLAIN");
-  pn_sasl_send(sasl0, iresp, size);
-  free(iresp);
-}
-
-void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  if (sasl) {
-    sasl->outcome = outcome;
-    // If we do this on the client it is a hack to tell us that
-    // no actual negatiation is going to happen and we can go
-    // straight to the AMQP layer
-    if (sasl->client) {
-      sasl->rcvd_done = true;
-      sasl->sent_done = true;
-    }
-    pni_emit(sasl0);
-  }
-}
-
-pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0)
-{
-  pni_sasl_t *sasl = get_sasl_internal(sasl0);
-  return sasl ? sasl->outcome : PN_SASL_NONE;
-}
-
-void pn_sasl_free(pn_transport_t *transport)
-{
-  if (transport) {
-    pni_sasl_t *sasl = transport->sasl;
-    if (sasl) {
-      free(sasl->mechanisms);
-      free(sasl->remote_mechanisms);
-      pn_buffer_free(sasl->send_data);
-      pn_buffer_free(sasl->recv_data);
-      free(sasl);
-    }
-  }
-}
-
-void pn_client_init(pn_transport_t *transport)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data);
-  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->mechanisms,
-                bytes.size, bytes.start);
-  pn_buffer_clear(sasl->send_data);
-  pni_emit((pn_sasl_t *) transport);
-}
-
-void pn_server_init(pn_transport_t *transport)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  // XXX
-  char *mechs[16];
-  int count = 0;
-
-  if (sasl->mechanisms) {
-    char *start = sasl->mechanisms;
-    char *end = start;
-
-    while (*end) {
-      if (*end == ' ') {
-        if (start != end) {
-          *end = '\0';
-          mechs[count++] = start;
-        }
-        end++;
-        start = end;
-      } else {
-        end++;
-      }
-    }
-
-    if (start != end) {
-      mechs[count++] = start;
-    }
-  }
-
-  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs);
-  pni_emit((pn_sasl_t *) transport);
-}
-
-void pn_server_done(pn_sasl_t *sasl0)
-{
-  pn_transport_t *transport = get_transport_internal(sasl0);
-  pni_sasl_t *sasl = transport->sasl;
-  pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome);
-  pni_emit(sasl0);
-}
-
-void pn_sasl_process(pn_transport_t *transport)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  if (!sasl->sent_init) {
-    if (sasl->client) {
-      pn_client_init(transport);
-    } else {
-      pn_server_init(transport);
-    }
-    sasl->sent_init = true;
-  }
-
-  if (pn_buffer_size(sasl->send_data)) {
-    pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data);
-    pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", sasl->client ? SASL_RESPONSE : SASL_CHALLENGE,
-                  bytes.size, bytes.start);
-    pn_buffer_clear(sasl->send_data);
-    pni_emit((pn_sasl_t *) transport);
-  }
-
-  if (!sasl->client && sasl->outcome != PN_SASL_NONE && !sasl->sent_done) {
-    pn_server_done((pn_sasl_t *)transport);
-    sasl->sent_done = true;
-  }
-
-  // XXX: need to finish this check when challenge/response is complete
-  //      check for client is outome is received
-  //      check for server is that there are no pending frames (either init
-  //      or challenges) from client
-  if (!sasl->client && sasl->sent_done && sasl->rcvd_init) {
-    sasl->rcvd_done = true;
-    sasl->halt = true;
-  }
-}
-
-ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt);
-  if (n < 0) return n;
-
-  pn_sasl_process(transport);
-
-  if (sasl->rcvd_done) {
-    if (pn_sasl_state((pn_sasl_t *)transport) == PN_SASL_PASS) {
-      if (n) {
-        return n;
-      } else {
-        return PN_EOS;
-      }
-    } else {
-      // XXX: should probably do something better here
-      return PN_ERR;
-    }
-  } else {
-    return n;
-  }
-}
-
-ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size)
-{
-  pn_sasl_process(transport);
-
-  pni_sasl_t *sasl = transport->sasl;
-  if (transport->available == 0 && sasl->sent_done) {
-    if (pn_sasl_state((pn_sasl_t *)transport) == PN_SASL_PASS) {
-      return PN_EOS;
-    } else {
-      // XXX: should probably do something better here
-      return PN_ERR;
-    }
-  } else {
-    return pn_dispatcher_output(transport, bytes, size);
-  }
-}
-
-int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  assert(sasl && !sasl->client);
-  pn_bytes_t mech;
-  pn_bytes_t recv;
-  int err = pn_data_scan(args, "D.[sz]", &mech, &recv);
-  if (err) return err;
-  sasl->remote_mechanisms = pn_strndup(mech.start, mech.size);
-  pn_buffer_append(sasl->recv_data, recv.start, recv.size);
-  sasl->rcvd_init = true;
-  return 0;
-}
-
-int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  assert(sasl && sasl->client);
-  sasl->rcvd_init = true;
-  return 0;
-}
-
-int pn_do_recv(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  pn_bytes_t recv;
-  int err = pn_data_scan(args, "D.[z]", &recv);
-  if (err) return err;
-  pn_buffer_append(sasl->recv_data, recv.start, recv.size);
-  return 0;
-}
-
-int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-#ifndef NDEBUG                  /* Avoid unused variable warnings in release builds. */
-  pni_sasl_t *sasl = transport->sasl;
-  assert(sasl && sasl->client);
-#endif
-  return pn_do_recv(transport, frame_type, channel, args, payload);
-}
-
-int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-#ifndef NDEBUG                  /* Avoid unused variable warnings in release builds. */
-  pni_sasl_t *sasl = transport->sasl;
-  assert(sasl && !sasl->client);
-#endif
-  return pn_do_recv(transport, frame_type, channel, args, payload);
-}
-
-int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload)
-{
-  pni_sasl_t *sasl = transport->sasl;
-  assert(sasl && sasl->client);
-  uint8_t outcome;
-  int err = pn_data_scan(args, "D.[B]", &outcome);
-  if (err) return err;
-  sasl->outcome = (pn_sasl_outcome_t) outcome;
-  sasl->rcvd_done = true;
-  sasl->sent_done = true;
-  sasl->halt = true;
-  pni_emit((pn_sasl_t *) transport);
-  return 0;
-}
-
 #define SASL_HEADER ("AMQP\x03\x01\x00\x00")
 #define SASL_HEADER_LEN 8
 
@@ -504,23 +95,32 @@ static ssize_t pn_input_read_sasl_header(pn_transport_t* transport, unsigned int
   default:
     break;
   }
+  transport->close_sent = true;
   char quoted[1024];
   pn_quote_data(quoted, 1024, bytes, available);
   pn_do_error(transport, "amqp:connection:framing-error",
               "%s header mismatch: %s ['%s']%s", "SASL", pni_protocol_name(protocol), quoted,
               !eos ? "" : " (connection aborted)");
+  pn_set_error_layer(transport);
   return PN_EOS;
 }
 
 static ssize_t pn_input_read_sasl(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available)
 {
-  pni_sasl_t *sasl = transport->sasl;
-  if (!sasl->input_bypass) {
+  bool eos = pn_transport_capacity(transport)==PN_EOS;
+  if (eos) {
+    transport->close_sent = true;
+    pn_do_error(transport, "amqp:connection:framing-error", "connection aborted");
+    pn_set_error_layer(transport);
+    return PN_EOS;
+  }
+
+  if (!transport->sasl_input_bypass) {
     ssize_t n = pn_sasl_input(transport, bytes, available);
     if (n != PN_EOS) return n;
 
-    sasl->input_bypass = true;
-    if (sasl->output_bypass)
+    transport->sasl_input_bypass = true;
+    if (transport->sasl_output_bypass)
         transport->io_layers[layer] = &pni_passthru_layer;
   }
   return pni_passthru_layer.process_input(transport, layer, bytes, available );
@@ -542,8 +142,7 @@ static ssize_t pn_output_write_sasl_header(pn_transport_t *transport, unsigned i
 
 static ssize_t pn_output_write_sasl(pn_transport_t* transport, unsigned int layer, char* bytes, size_t available)
 {
-  pni_sasl_t *sasl = transport->sasl;
-  if (!sasl->output_bypass) {
+  if (!transport->sasl_output_bypass) {
     // this accounts for when pn_do_error is invoked, e.g. by idle timeout
     ssize_t n;
     if (transport->close_sent) {
@@ -553,8 +152,8 @@ static ssize_t pn_output_write_sasl(pn_transport_t* transport, unsigned int laye
     }
     if (n != PN_EOS) return n;
 
-    sasl->output_bypass = true;
-    if (sasl->input_bypass)
+    transport->sasl_output_bypass = true;
+    if (transport->sasl_input_bypass)
         transport->io_layers[layer] = &pni_passthru_layer;
   }
   return pni_passthru_layer.process_output(transport, layer, bytes, available );

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/ssl/openssl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/ssl/openssl.c b/proton-c/src/ssl/openssl.c
index b9331d5..42bbd71 100644
--- a/proton-c/src/ssl/openssl.c
+++ b/proton-c/src/ssl/openssl.c
@@ -807,6 +807,13 @@ pn_ssl_t *pn_ssl(pn_transport_t *transport)
 
   transport->ssl = ssl;
 
+  // Set up hostname from any bound connection
+  if (transport->connection) {
+    if (pn_string_size(transport->connection->hostname)) {
+      pn_ssl_set_peer_hostname((pn_ssl_t *) transport, pn_string_get(transport->connection->hostname));
+    }
+  }
+
   return (pn_ssl_t *) transport;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4a09c6a1/proton-c/src/tests/reactor.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/reactor.c b/proton-c/src/tests/reactor.c
index 8896c2c..fe6c769 100644
--- a/proton-c/src/tests/reactor.c
+++ b/proton-c/src/tests/reactor.c
@@ -288,7 +288,7 @@ static void test_reactor_connect(void) {
   pn_reactor_connection(reactor, ch);
   pn_reactor_run(reactor);
   expect(srv->events, PN_CONNECTION_INIT, PN_CONNECTION_BOUND,
-         PN_TRANSPORT, PN_CONNECTION_REMOTE_OPEN,
+         PN_CONNECTION_REMOTE_OPEN,
          PN_CONNECTION_LOCAL_OPEN, PN_TRANSPORT,
          PN_CONNECTION_REMOTE_CLOSE, PN_TRANSPORT_TAIL_CLOSED,
          PN_CONNECTION_LOCAL_CLOSE, PN_TRANSPORT,
@@ -297,7 +297,7 @@ static void test_reactor_connect(void) {
   pn_free(srv->events);
   pn_decref(sh);
   expect(cli->events, PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
-         PN_CONNECTION_BOUND, PN_TRANSPORT, PN_TRANSPORT,
+         PN_CONNECTION_BOUND,
          PN_CONNECTION_REMOTE_OPEN, PN_CONNECTION_LOCAL_CLOSE,
          PN_TRANSPORT, PN_TRANSPORT_HEAD_CLOSED,
          PN_CONNECTION_REMOTE_CLOSE, PN_TRANSPORT_TAIL_CLOSED,


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


Mime
View raw message