You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gs...@apache.org on 2014/11/26 21:06:01 UTC
[14/35] qpid-proton git commit: PROTON-749: Server transport
autodetect: - Defer layer initialisation until first send/receive - Move
layer initialisation entirely to transport - Server transports will now
autodetect the client protocol layers talking
PROTON-749: Server transport autodetect:
- Defer layer initialisation until first send/receive
- Move layer initialisation entirely to transport
- Server transports will now autodetect the client protocol layers
talking to them and configure themselves automatically
- There are still some hacks in here to make SSL work.
It's not obvious why we need these hacks.
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/1b2be03c
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/1b2be03c
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/1b2be03c
Branch: refs/heads/examples
Commit: 1b2be03c748ef5a57cf181f8373b9b6e8f8cfd22
Parents: 9c9872b
Author: Andrew Stitcher <as...@apache.org>
Authored: Wed Aug 20 23:39:34 2014 -0400
Committer: Andrew Stitcher <as...@apache.org>
Committed: Wed Nov 19 17:50:21 2014 -0500
----------------------------------------------------------------------
proton-c/CMakeLists.txt | 1 +
proton-c/src/engine/engine-internal.h | 8 +-
proton-c/src/sasl/sasl-internal.h | 2 +
proton-c/src/sasl/sasl.c | 55 +++----
proton-c/src/ssl/openssl.c | 112 +------------
proton-c/src/ssl/ssl-internal.h | 2 +
proton-c/src/ssl/ssl_stub.c | 17 +-
proton-c/src/transport/autodetect.c | 135 ++++++++++++++++
proton-c/src/transport/autodetect.h | 40 +++++
proton-c/src/transport/transport.c | 249 +++++++++++++++++++++++------
proton-c/src/windows/schannel.c | 113 +------------
11 files changed, 428 insertions(+), 306 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index 6b6b730..b09e1c4 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -297,6 +297,7 @@ set (qpid-proton-core
src/dispatcher/dispatcher.c
src/engine/engine.c
src/events/event.c
+ src/transport/autodetect.c
src/transport/transport.c
src/message/message.c
src/sasl/sasl.c
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/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 ab66ef5..f53e88b 100644
--- a/proton-c/src/engine/engine-internal.h
+++ b/proton-c/src/engine/engine-internal.h
@@ -107,6 +107,9 @@ typedef struct pn_io_layer_t {
} pn_io_layer_t;
extern const pn_io_layer_t pni_passthru_layer;
+extern const pn_io_layer_t ssl_layer;
+extern const pn_io_layer_t sasl_header_layer;
+extern const pn_io_layer_t sasl_write_header_layer;
typedef struct pni_sasl_t pni_sasl_t;
typedef struct pni_ssl_t pni_ssl_t;
@@ -131,10 +134,7 @@ struct pn_transport_t {
pn_condition_t condition;
pn_error_t *error;
-#define PN_IO_SSL 0
-#define PN_IO_SASL 1
-#define PN_IO_AMQP 2
-#define PN_IO_LAYER_CT (PN_IO_AMQP+1)
+#define PN_IO_LAYER_CT 3
const pn_io_layer_t *io_layers[PN_IO_LAYER_CT];
/* dead remote detection */
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/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 15fd0b1..ca4c80e 100644
--- a/proton-c/src/sasl/sasl-internal.h
+++ b/proton-c/src/sasl/sasl-internal.h
@@ -57,4 +57,6 @@ void pn_sasl_trace(pn_transport_t *transport, pn_trace_t trace);
*/
void pn_sasl_free(pn_transport_t *transport);
+bool pn_sasl_skipping_allowed(pn_transport_t *transport);
+
#endif /* sasl-internal.h */
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/sasl/sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl.c b/proton-c/src/sasl/sasl.c
index 90adcf6..1ee8f9b 100644
--- a/proton-c/src/sasl/sasl.c
+++ b/proton-c/src/sasl/sasl.c
@@ -32,6 +32,7 @@
#include "engine/engine-internal.h"
#include "dispatcher/dispatcher.h"
#include "util.h"
+#include "transport/autodetect.h"
struct pni_sasl_t {
@@ -68,7 +69,7 @@ static ssize_t pn_input_read_sasl(pn_transport_t *transport, unsigned int layer,
static ssize_t pn_output_write_sasl_header(pn_transport_t* transport, unsigned int layer, char* bytes, size_t size);
static ssize_t pn_output_write_sasl(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available);
-const pn_io_layer_t sasl_headers_layer = {
+const pn_io_layer_t sasl_header_layer = {
pn_input_read_sasl_header,
pn_output_write_sasl_header,
NULL,
@@ -118,7 +119,6 @@ pn_sasl_t *pn_sasl(pn_transport_t *transport)
sasl->output_bypass = false;
transport->sasl = sasl;
- transport->io_layers[PN_IO_SASL] = &sasl_headers_layer;
}
// The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t
@@ -204,6 +204,11 @@ void pn_sasl_allow_skip(pn_sasl_t *sasl0, bool allow)
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);
@@ -441,45 +446,33 @@ int pn_do_outcome(pn_dispatcher_t *disp)
}
#define SASL_HEADER ("AMQP\x03\x01\x00\x00")
-#define AMQP_HEADER ("AMQP\x00\x01\x00\x00")
#define SASL_HEADER_LEN 8
static ssize_t pn_input_read_sasl_header(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available)
{
- pni_sasl_t *sasl = transport->sasl;
- if (available > 0) {
- if (available < SASL_HEADER_LEN) {
- if (memcmp(bytes, SASL_HEADER, available) == 0 ||
- memcmp(bytes, AMQP_HEADER, available) == 0)
- return 0;
+ bool eos = pn_transport_capacity(transport)==PN_EOS;
+ pni_protocol_type_t protocol = pni_sniff_header(bytes, available);
+ switch (protocol) {
+ case PNI_PROTOCOL_AMQP_SASL:
+ if (transport->io_layers[layer] == &sasl_read_header_layer) {
+ transport->io_layers[layer] = &sasl_layer;
} else {
- if (memcmp(bytes, SASL_HEADER, SASL_HEADER_LEN) == 0) {
- if (transport->io_layers[layer] == &sasl_read_header_layer) {
- transport->io_layers[layer] = &sasl_layer;
- } else {
- transport->io_layers[layer] = &sasl_write_header_layer;
- }
- if (sasl->disp->trace & PN_TRACE_FRM)
- pn_transport_logf(transport, " <- %s", "SASL");
- return SASL_HEADER_LEN;
- }
- if (memcmp(bytes, AMQP_HEADER, SASL_HEADER_LEN) == 0) {
- if (sasl->allow_skip) {
- sasl->outcome = PN_SASL_SKIPPED;
- transport->io_layers[layer] = &pni_passthru_layer;
- return pni_passthru_layer.process_input(transport, layer, bytes, available);
- } else {
- pn_do_error(transport, "amqp:connection:policy-error",
- "Client skipped SASL exchange - forbidden");
- return PN_EOS;
- }
- }
+ transport->io_layers[layer] = &sasl_write_header_layer;
}
+ if (transport->sasl->disp->trace & PN_TRACE_FRM)
+ pn_transport_logf(transport, " <- %s", "SASL");
+ return SASL_HEADER_LEN;
+ case PNI_PROTOCOL_INSUFFICIENT:
+ if (!eos) return 0;
+ /* Fallthru */
+ default:
+ break;
}
char quoted[1024];
pn_quote_data(quoted, 1024, bytes, available);
pn_do_error(transport, "amqp:connection:framing-error",
- "%s header mismatch: '%s'", "SASL", quoted);
+ "%s header mismatch: %s ['%s']%s", "SASL", pni_protocol_name(protocol), quoted,
+ !eos ? "" : " (connection aborted)");
return PN_EOS;
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/ssl/openssl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/ssl/openssl.c b/proton-c/src/ssl/openssl.c
index 41e36b5..0562cae 100644
--- a/proton-c/src/ssl/openssl.c
+++ b/proton-c/src/ssl/openssl.c
@@ -60,7 +60,6 @@
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_domain_t {
@@ -145,11 +144,8 @@ struct pn_ssl_session_t {
static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata);
static ssize_t process_input_ssl( pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
static ssize_t process_output_ssl( pn_transport_t *transport, unsigned int layer, char *input_data, size_t len);
-static ssize_t process_input_unknown( pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
-static ssize_t process_output_unknown( pn_transport_t *transport, unsigned int layer, char *input_data, size_t len);
static ssize_t process_input_done(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
static ssize_t process_output_done(pn_transport_t *transport, unsigned int layer, 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_transport_t *, pni_ssl_t *);
static void release_ssl_socket( pni_ssl_t * );
static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *, const char * );
@@ -682,13 +678,6 @@ int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t *domain,
return 0;
}
-const pn_io_layer_t unknown_layer = {
- process_input_unknown,
- process_output_unknown,
- NULL,
- NULL
-};
-
const pn_io_layer_t ssl_layer = {
process_input_ssl,
process_output_ssl,
@@ -725,11 +714,6 @@ int pn_ssl_init(pn_ssl_t *ssl0, pn_ssl_domain_t *domain, const char *session_id)
ssl->domain = domain;
domain->ref_count++;
- if (domain->allow_unsecured) {
- transport->io_layers[PN_IO_SSL] = &unknown_layer;
- } else {
- transport->io_layers[PN_IO_SSL] = &ssl_layer;
- }
if (session_id && domain->mode == PN_SSL_MODE_CLIENT)
ssl->session_id = pn_strdup(session_id);
@@ -748,6 +732,10 @@ int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain)
return 0;
}
+bool pn_ssl_allow_unsecured(pn_transport_t *transport)
+{
+ return transport && transport->ssl && transport->ssl->domain && transport->ssl->domain->allow_unsecured;
+}
bool pn_ssl_get_cipher_name(pn_ssl_t *ssl0, char *buffer, size_t size )
{
@@ -862,13 +850,6 @@ static int start_ssl_shutdown(pn_transport_t *transport)
}
-
-static int setup_ssl_connection(pn_transport_t *transport, unsigned int layer)
-{
- transport->io_layers[layer] = &ssl_layer;
- return 0;
-}
-
//////// SSL Connections
@@ -1213,91 +1194,6 @@ static void release_ssl_socket(pni_ssl_t *ssl)
}
-static int setup_cleartext_connection(pn_transport_t *transport, unsigned int layer)
-{
- transport->io_layers[layer] = &pni_passthru_layer;
- return 0;
-}
-
-
-// until we determine if the client is using SSL or not:
-
-static ssize_t process_input_unknown(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len)
-{
- switch (check_for_ssl_connection( input_data, len )) {
- case SSL_CONNECTION:
- ssl_log( transport, "SSL connection detected.");
- setup_ssl_connection(transport, layer);
- break;
- case CLEAR_CONNECTION:
- ssl_log( transport, "Cleartext connection detected.");
- setup_cleartext_connection(transport, layer);
- break;
- default:
- return 0;
- }
- return transport->io_layers[layer]->process_input(transport, layer, input_data, len );
-}
-
-static ssize_t process_output_unknown(pn_transport_t *transport, unsigned int layer, char *input_data, size_t len)
-{
- // do not do output until we know if SSL is used or not
- return 0;
-}
-
-static connection_mode_t check_for_ssl_connection( const char *data, size_t len )
-{
- if (len >= 5) {
- const unsigned char *buf = (unsigned char *)data;
- /*
- * SSLv2 Client Hello format
- * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
- *
- * Bytes 0-1: RECORD-LENGTH
- * Byte 2: MSG-CLIENT-HELLO (1)
- * Byte 3: CLIENT-VERSION-MSB
- * Byte 4: CLIENT-VERSION-LSB
- *
- * Allowed versions:
- * 2.0 - SSLv2
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- *
- * The version sent in the Client-Hello is the latest version supported by
- * the client. NSS may send version 3.x in an SSLv2 header for
- * maximum compatibility.
- */
- int isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
- ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
- (buf[3] == 2 && buf[4] == 0)); // SSL 2
-
- /*
- * SSLv3/TLS Client Hello format
- * RFC 2246
- *
- * Byte 0: ContentType (handshake - 22)
- * Bytes 1-2: ProtocolVersion {major, minor}
- *
- * Allowed versions:
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- */
- int isSSL3Handshake = buf[0] == 22 && // handshake
- (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
-
- if (isSSL2Handshake || isSSL3Handshake) {
- return SSL_CONNECTION;
- } else {
- return CLEAR_CONNECTION;
- }
- }
- return UNKNOWN_CONNECTION;
-}
-
void pn_ssl_trace(pn_transport_t *transport, pn_trace_t trace)
{
transport->ssl->trace = trace;
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/ssl/ssl-internal.h
----------------------------------------------------------------------
diff --git a/proton-c/src/ssl/ssl-internal.h b/proton-c/src/ssl/ssl-internal.h
index f1cd637..9430af0 100644
--- a/proton-c/src/ssl/ssl-internal.h
+++ b/proton-c/src/ssl/ssl-internal.h
@@ -33,4 +33,6 @@ void pn_ssl_free(pn_transport_t *transport);
void pn_ssl_trace(pn_transport_t *transport, pn_trace_t trace);
+bool pn_ssl_allow_unsecured(pn_transport_t *transport);
+
#endif /* ssl-internal.h */
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/ssl/ssl_stub.c
----------------------------------------------------------------------
diff --git a/proton-c/src/ssl/ssl_stub.c b/proton-c/src/ssl/ssl_stub.c
index a5fce02..cea5dc4 100644
--- a/proton-c/src/ssl/ssl_stub.c
+++ b/proton-c/src/ssl/ssl_stub.c
@@ -22,6 +22,7 @@
#include <proton/ssl.h>
#include <proton/error.h>
#include <proton/transport.h>
+#include "engine/engine-internal.h"
/** @file
@@ -55,16 +56,23 @@ void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace)
{
}
-ssize_t pn_ssl_input(pn_ssl_t *ssl, const char *bytes, size_t available)
+ssize_t pn_ssl_input(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available)
{
return PN_EOS;
}
-ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size)
+ssize_t pn_ssl_output(pn_transport_t *transport, unsigned int layer, char *buffer, size_t max_size)
{
return PN_EOS;
}
+const pn_io_layer_t ssl_layer = {
+ pn_ssl_input,
+ pn_ssl_output,
+ NULL,
+ NULL
+};
+
bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size)
{
return false;
@@ -110,6 +118,11 @@ int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain)
return -1;
}
+bool pn_ssl_allow_unsecured(pn_ssl_t *ssl)
+{
+ return true;
+}
+
pn_ssl_resume_status_t pn_ssl_resume_status( pn_ssl_t *s )
{
return PN_SSL_RESUME_UNKNOWN;
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/transport/autodetect.c
----------------------------------------------------------------------
diff --git a/proton-c/src/transport/autodetect.c b/proton-c/src/transport/autodetect.c
new file mode 100644
index 0000000..00f6d98
--- /dev/null
+++ b/proton-c/src/transport/autodetect.c
@@ -0,0 +1,135 @@
+/*
+ *
+ * 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 "autodetect.h"
+
+#define SASL_HEADER ("AMQP\x03\x01\x00\x00")
+#define SSL_HEADER ("AMQP\x02\x01\x00\x00")
+#define AMQP_HEADER ("AMQP\x00\x01\x00\x00")
+
+#define SASL_HEADER_LEN 8
+
+/*
+ * SSLv2 Client Hello format
+ * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
+ *
+ * Bytes 0-1: RECORD-LENGTH
+ * Byte 2: MSG-CLIENT-HELLO (1)
+ * Byte 3: CLIENT-VERSION-MSB
+ * Byte 4: CLIENT-VERSION-LSB
+ *
+ * Allowed versions:
+ * 2.0 - SSLv2
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ *
+ * The version sent in the Client-Hello is the latest version supported by
+ * the client. NSS may send version 3.x in an SSLv2 header for
+ * maximum compatibility.
+ */
+/*
+ * SSLv3/TLS Client Hello format
+ * RFC 2246
+ *
+ * Byte 0: ContentType (handshake - 22)
+ * Bytes 1-2: ProtocolVersion {major, minor}
+ *
+ * Allowed versions:
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ */
+/*
+ * AMQP 1.0 Header
+ *
+ * Bytes 0-3: "AMQP"
+ * Byte 4: 0==AMQP, 2==SSL, 3==SASL
+ * Byte 5: 1
+ * Bytes 6-7: 0
+ */
+/*
+ * AMQP Pre 1.0 Header
+ *
+ * Bytes 0-3: 'AMQP'
+ * Byte 4: 1
+ * Byte 5: 1
+ * Byte 6: 0 (major version)
+ * Byte 7: Minor version
+ */
+pni_protocol_type_t pni_sniff_header(const char *buf, size_t len)
+{
+ if (len < 3) return PNI_PROTOCOL_INSUFFICIENT;
+ bool isSSL3Handshake = buf[0]==22 && // handshake
+ buf[1]==3 && buf[2]<=3; // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+ if (isSSL3Handshake) return PNI_PROTOCOL_SSL;
+
+ bool isFirst3AMQP = buf[0]=='A' && buf[1]=='M' && buf[2]=='Q';
+ bool isFirst3SSL2CLientHello = buf[2]==1; // Client Hello
+ if (!isFirst3AMQP && !isFirst3SSL2CLientHello) return PNI_PROTOCOL_UNKNOWN;
+
+
+ if (len < 4) return PNI_PROTOCOL_INSUFFICIENT;
+ bool isAMQP = isFirst3AMQP && buf[3]=='P';
+ bool isFirst4SSL2ClientHello = isFirst3SSL2CLientHello && (buf[3]==2 || buf[3]==3);
+ if (!isAMQP && !isFirst4SSL2ClientHello) return PNI_PROTOCOL_UNKNOWN;
+
+ if (len < 5) return PNI_PROTOCOL_INSUFFICIENT;
+ bool isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
+ ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+ (buf[3] == 2 && buf[4] == 0)); // SSL 2
+ if (isSSL2Handshake) return PNI_PROTOCOL_SSL;
+
+ bool isFirst5OldAMQP = isAMQP && buf[4]==1;
+ bool isFirst5AMQP = isAMQP && (buf[4]==0 || buf[4]==2 || buf[4]==3);
+ if (!isFirst5AMQP && !isFirst5OldAMQP) return PNI_PROTOCOL_UNKNOWN;
+
+ if (len < 6) return PNI_PROTOCOL_INSUFFICIENT;
+
+ // Both old and new versions of AMQP have 1 in byte 5
+ if (buf[5]!=1) return PNI_PROTOCOL_UNKNOWN;
+
+ // From here on it must be some sort of AMQP
+ if (len < 8) return PNI_PROTOCOL_INSUFFICIENT;
+ if (buf[6]==0 && buf[7]==0) {
+ // AM<QP 1.0
+ if (buf[4]==0) return PNI_PROTOCOL_AMQP1;
+ if (buf[4]==2) return PNI_PROTOCOL_AMQP_SSL;
+ if (buf[4]==3) return PNI_PROTOCOL_AMQP_SASL;
+ }
+ return PNI_PROTOCOL_AMQP_OTHER;
+}
+
+const char* pni_protocol_name(pni_protocol_type_t p)
+{
+ static const char* names[] = {
+ "Insufficient data to determine protocol",
+ "Unknown protocol",
+ "SSL/TLS connection",
+ "AMQP TLS layer",
+ "AMQP SASL layer",
+ "AMQP 1.0 layer",
+ "Pre standard AMQP connection"
+ };
+ return names[p];
+}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/transport/autodetect.h
----------------------------------------------------------------------
diff --git a/proton-c/src/transport/autodetect.h b/proton-c/src/transport/autodetect.h
new file mode 100644
index 0000000..12cb7d8
--- /dev/null
+++ b/proton-c/src/transport/autodetect.h
@@ -0,0 +1,40 @@
+#ifndef PROTON_AUTODETECT_H
+#define PROTON_AUTODETECT_H 1
+
+/*
+ *
+ * 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 "proton/types.h"
+
+typedef enum {
+ PNI_PROTOCOL_INSUFFICIENT,
+ PNI_PROTOCOL_UNKNOWN,
+ PNI_PROTOCOL_SSL,
+ PNI_PROTOCOL_AMQP_SSL,
+ PNI_PROTOCOL_AMQP_SASL,
+ PNI_PROTOCOL_AMQP1,
+ PNI_PROTOCOL_AMQP_OTHER
+} pni_protocol_type_t;
+
+pni_protocol_type_t pni_sniff_header(const char *data, size_t len);
+const char* pni_protocol_name(pni_protocol_type_t p);
+
+#endif
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/transport/transport.c
----------------------------------------------------------------------
diff --git a/proton-c/src/transport/transport.c b/proton-c/src/transport/transport.c
index 2d5f93a..67fd3ab 100644
--- a/proton-c/src/transport/transport.c
+++ b/proton-c/src/transport/transport.c
@@ -19,22 +19,25 @@
*
*/
+#include "proton/framing.h"
+
#include "engine/engine-internal.h"
-#include <stdlib.h>
-#include <string.h>
-#include <proton/framing.h>
+#include "sasl/sasl-internal.h"
+#include "ssl/ssl-internal.h"
+
+#include "autodetect.h"
#include "protocol.h"
#include "dispatch_actions.h"
+#include "proton/event.h"
+#include "platform.h"
+#include "platform_fmt.h"
+#include <stdlib.h>
+#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
-#include "sasl/sasl-internal.h"
-#include "ssl/ssl-internal.h"
-#include "platform.h"
-#include "platform_fmt.h"
-
static ssize_t transport_consume(pn_transport_t *transport);
// delivery buffers
@@ -92,26 +95,25 @@ void pn_delivery_map_clear(pn_delivery_map_t *dm)
dm->next = 0;
}
+static void pni_default_tracer(pn_transport_t *transport, const char *message)
+{
+ fprintf(stderr, "[%p]:%s\n", (void *) transport, message);
+}
+
static ssize_t pn_io_layer_input_passthru(pn_transport_t *, unsigned int, const char *, size_t );
static ssize_t pn_io_layer_output_passthru(pn_transport_t *, unsigned int, char *, size_t );
+static ssize_t pn_io_layer_input_setup(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available);
+static ssize_t pn_io_layer_output_setup(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available);
+
static ssize_t pn_input_read_amqp_header(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available);
static ssize_t pn_input_read_amqp(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available);
static ssize_t pn_output_write_amqp_header(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available);
static ssize_t pn_output_write_amqp(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available);
static pn_timestamp_t pn_tick_amqp(pn_transport_t *transport, unsigned int layer, pn_timestamp_t now);
-static void pni_default_tracer(pn_transport_t *transport, const char *message)
-{
- fprintf(stderr, "[%p]:%s\n", (void *) transport, message);
-}
-
-const pn_io_layer_t pni_passthru_layer = {
- pn_io_layer_input_passthru,
- pn_io_layer_output_passthru,
- NULL,
- NULL
-};
+static ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available);
+static ssize_t pn_io_layer_output_null(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available);
const pn_io_layer_t amqp_header_layer = {
pn_input_read_amqp_header,
@@ -141,6 +143,158 @@ const pn_io_layer_t amqp_layer = {
NULL
};
+const pn_io_layer_t pni_setup_layer = {
+ pn_io_layer_input_setup,
+ pn_io_layer_output_setup,
+ NULL,
+ NULL
+};
+
+const pn_io_layer_t pni_autodetect_layer = {
+ pn_io_layer_input_autodetect,
+ pn_io_layer_output_null,
+ NULL,
+ NULL
+};
+
+const pn_io_layer_t pni_passthru_layer = {
+ pn_io_layer_input_passthru,
+ pn_io_layer_output_passthru,
+ NULL,
+ NULL
+};
+
+/* Set up the transport protocol layers depending on what is configured */
+static void pn_io_layer_setup(pn_transport_t *transport, unsigned int layer)
+{
+ assert(layer == 0);
+ // Figure out if we are server or not
+ if (transport->server)
+ {
+ // XXX: This is currently a large hack to work around the SSL
+ // code not handling a connection error before being set up fully
+ if (transport->ssl && pn_ssl_allow_unsecured(transport)) {
+ transport->io_layers[layer++] = &pni_autodetect_layer;
+ return;
+ }
+ }
+ if (transport->ssl) {
+ transport->io_layers[layer++] = &ssl_layer;
+ }
+ if (transport->server) {
+ transport->io_layers[layer++] = &pni_autodetect_layer;
+ return;
+ }
+ if (transport->sasl) {
+ transport->io_layers[layer++] = &sasl_header_layer;
+ }
+ transport->io_layers[layer++] = &amqp_header_layer;
+}
+
+ssize_t pn_io_layer_input_setup(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available)
+{
+ pn_io_layer_setup(transport, layer);
+ return transport->io_layers[layer]->process_input(transport, layer, bytes, available);
+}
+
+ssize_t pn_io_layer_output_setup(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available)
+{
+ pn_io_layer_setup(transport, layer);
+ return transport->io_layers[layer]->process_output(transport, layer, bytes, available);
+}
+
+// Autodetect the layer by reading the protocol header
+ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available)
+{
+ const char* error;
+ bool eos = pn_transport_capacity(transport)==PN_EOS;
+ if (eos && available==0) {
+ pn_do_error(transport, "amqp:connection:framing-error", "No valid protocol header found");
+ return PN_EOS;
+ }
+ pni_protocol_type_t protocol = pni_sniff_header(bytes, available);
+ if (transport->disp->trace & PN_TRACE_DRV)
+ pn_transport_logf(transport, "%s detected", pni_protocol_name(protocol));
+ switch (protocol) {
+ case PNI_PROTOCOL_SSL:
+ if (!transport->ssl) {
+ pn_ssl(transport);
+ }
+ transport->io_layers[layer] = &ssl_layer;
+ transport->io_layers[layer+1] = &pni_autodetect_layer;
+ return ssl_layer.process_input(transport, layer, bytes, available);
+ case PNI_PROTOCOL_AMQP_SSL:
+ if (!transport->ssl) {
+ pn_ssl(transport);
+ }
+ transport->io_layers[layer] = &ssl_layer;
+ transport->io_layers[layer+1] = &pni_autodetect_layer;
+ return 8;
+ case PNI_PROTOCOL_AMQP_SASL:
+ if (!transport->sasl) {
+ pn_sasl(transport);
+ }
+ transport->io_layers[layer] = &sasl_write_header_layer;
+ transport->io_layers[layer+1] = &pni_autodetect_layer;
+ if (transport->disp->trace & PN_TRACE_FRM)
+ pn_transport_logf(transport, " <- %s", "SASL");
+ return 8;
+ case PNI_PROTOCOL_AMQP1:
+ if (transport->sasl && pn_sasl_state((pn_sasl_t *)transport)==PN_SASL_IDLE) {
+ if (pn_sasl_skipping_allowed(transport)) {
+ pn_sasl_done((pn_sasl_t *)transport, PN_SASL_SKIPPED);
+ } else {
+ pn_do_error(transport, "amqp:connection:policy-error",
+ "Client skipped SASL exchange - forbidden");
+ return PN_EOS;
+ }
+ }
+ transport->io_layers[layer] = &amqp_write_header_layer;
+ if (transport->disp->trace & PN_TRACE_FRM)
+ pn_transport_logf(transport, " <- %s", "AMQP");
+ return 8;
+ case PNI_PROTOCOL_INSUFFICIENT:
+ if (!eos) return 0;
+ error = "End of input stream before protocol detection";
+ break;
+ case PNI_PROTOCOL_AMQP_OTHER:
+ error = "Incompatible AMQP connection detected";
+ break;
+ case PNI_PROTOCOL_UNKNOWN:
+ default:
+ error = "Unknown protocol detected";
+ break;
+ }
+ char quoted[1024];
+ pn_quote_data(quoted, 1024, bytes, available);
+ pn_do_error(transport, "amqp:connection:framing-error",
+ "%s: '%s'%s", error, quoted,
+ !eos ? "" : " (connection aborted)");
+ return PN_EOS;
+}
+
+// We don't know what the output should be - do nothing
+ssize_t pn_io_layer_output_null(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available)
+{
+ return 0;
+}
+
+/** Pass through input handler */
+ssize_t pn_io_layer_input_passthru(pn_transport_t *transport, unsigned int layer, const char *data, size_t available)
+{
+ if (layer+1<PN_IO_LAYER_CT)
+ return transport->io_layers[layer+1]->process_input(transport, layer+1, data, available);
+ return PN_EOS;
+}
+
+/** Pass through output handler */
+ssize_t pn_io_layer_output_passthru(pn_transport_t *transport, unsigned int layer, char *data, size_t available)
+{
+ if (layer+1<PN_IO_LAYER_CT)
+ return transport->io_layers[layer+1]->process_output(transport, layer+1, data, available);
+ return PN_EOS;
+}
+
static void pn_transport_initialize(void *object)
{
pn_transport_t *transport = (pn_transport_t *)object;
@@ -157,9 +311,11 @@ static void pn_transport_initialize(void *object)
transport->connection = NULL;
for (int layer=0; layer<PN_IO_LAYER_CT; ++layer) {
- transport->io_layers[layer] = &pni_passthru_layer;
+ transport->io_layers[layer] = NULL;
}
- transport->io_layers[PN_IO_AMQP] = &amqp_header_layer;
+
+ // Defer setting up the layers until the first data arrives or is sent
+ transport->io_layers[0] = &pni_setup_layer;
transport->open_sent = false;
transport->open_rcvd = false;
@@ -1127,20 +1283,12 @@ static ssize_t transport_consume(pn_transport_t *transport)
return consumed;
}
-#define AMQP_HEADER ("AMQP\x00\x01\x00\x00")
-
static ssize_t pn_input_read_amqp_header(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available)
{
- unsigned readable = pn_min(8, available);
bool eos = pn_transport_capacity(transport)==PN_EOS;
- if (memcmp(bytes, AMQP_HEADER, readable) || (readable<8 && eos) ) {
- char quoted[1024];
- pn_quote_data(quoted, 1024, bytes, available);
- pn_do_error(transport, "amqp:connection:framing-error",
- "%s header mismatch: '%s'%s", "AMQP", quoted,
- !eos ? "" : " (connection aborted)");
- return PN_EOS;
- } else if (readable==8) {
+ pni_protocol_type_t protocol = pni_sniff_header(bytes, available);
+ switch (protocol) {
+ case PNI_PROTOCOL_AMQP1:
if (transport->io_layers[layer] == &amqp_read_header_layer) {
transport->io_layers[layer] = &amqp_layer;
} else {
@@ -1149,8 +1297,18 @@ static ssize_t pn_input_read_amqp_header(pn_transport_t* transport, unsigned int
if (transport->disp->trace & PN_TRACE_FRM)
pn_transport_logf(transport, " <- %s", "AMQP");
return 8;
+ case PNI_PROTOCOL_INSUFFICIENT:
+ if (!eos) return 0;
+ /* Fallthru */
+ default:
+ break;
}
- return 0;
+ char quoted[1024];
+ pn_quote_data(quoted, 1024, bytes, available);
+ pn_do_error(transport, "amqp:connection:framing-error",
+ "%s header mismatch: %s ['%s']%s", "AMQP", pni_protocol_name(protocol), quoted,
+ !eos ? "" : " (connection aborted)");
+ return PN_EOS;
}
static ssize_t pn_input_read_amqp(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available)
@@ -1842,6 +2000,8 @@ int pn_process(pn_transport_t *transport)
return 0;
}
+#define AMQP_HEADER ("AMQP\x00\x01\x00\x00")
+
static ssize_t pn_output_write_amqp_header(pn_transport_t* transport, unsigned int layer, char* bytes, size_t available)
{
if (transport->disp->trace & PN_TRACE_FRM)
@@ -2061,7 +2221,7 @@ pn_timestamp_t pn_transport_tick(pn_transport_t *transport, pn_timestamp_t now)
{
pn_timestamp_t r = 0;
for (int i = 0; i<PN_IO_LAYER_CT; ++i) {
- if (transport->io_layers[i]->process_tick)
+ if (transport->io_layers[i] && transport->io_layers[i]->process_tick)
r = pn_timestamp_min(r, transport->io_layers[i]->process_tick(transport, i, now));
}
return r;
@@ -2081,24 +2241,6 @@ uint64_t pn_transport_get_frames_input(const pn_transport_t *transport)
return 0;
}
-/** Pass through input handler */
-ssize_t pn_io_layer_input_passthru(pn_transport_t *transport, unsigned int layer, const char *data, size_t available)
-{
- if (layer+1<PN_IO_LAYER_CT)
- return transport->io_layers[layer+1]->process_input(transport, layer+1, data, available);
- return PN_EOS;
-}
-
-/** Pass through output handler */
-ssize_t pn_io_layer_output_passthru(pn_transport_t *transport, unsigned int layer, char *data, size_t available)
-{
- if (layer+1<PN_IO_LAYER_CT)
- return transport->io_layers[layer+1]->process_output(transport, layer+1, data, available);
- return PN_EOS;
-}
-
-///
-
// input
ssize_t pn_transport_capacity(pn_transport_t *transport) /* <0 == done */
{
@@ -2252,7 +2394,8 @@ bool pn_transport_quiesced(pn_transport_t *transport)
else if (pending > 0) return false;
// no pending at transport, but check if data is buffered in I/O layers
for (int layer = 0; layer<PN_IO_LAYER_CT; ++layer) {
- if (transport->io_layers[layer]->buffered_output &&
+ if (transport->io_layers[layer] &&
+ transport->io_layers[layer]->buffered_output &&
transport->io_layers[layer]->buffered_output( transport ))
return false;
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1b2be03c/proton-c/src/windows/schannel.c
----------------------------------------------------------------------
diff --git a/proton-c/src/windows/schannel.c b/proton-c/src/windows/schannel.c
index 231349c..e7ef5fd 100644
--- a/proton-c/src/windows/schannel.c
+++ b/proton-c/src/windows/schannel.c
@@ -148,11 +148,8 @@ struct pn_ssl_session_t {
static ssize_t process_input_ssl( pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
static ssize_t process_output_ssl( pn_transport_t *transport, unsigned int layer, char *input_data, size_t len);
-static ssize_t process_input_unknown( pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
-static ssize_t process_output_unknown( pn_transport_t *transport, unsigned int layer, char *input_data, size_t len);
static ssize_t process_input_done(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len);
static ssize_t process_output_done(pn_transport_t *transport, unsigned int layer, char *input_data, size_t len);
-static connection_mode_t check_for_ssl_connection( const char *data, size_t len );
static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *, const char * );
static void ssl_session_free( pn_ssl_session_t *);
static size_t buffered_output( pn_transport_t *transport );
@@ -360,13 +357,6 @@ int pn_ssl_domain_set_peer_authentication(pn_ssl_domain_t *domain,
return 0;
}
-const pn_io_layer_t unknown_layer = {
- process_input_unknown,
- process_output_unknown,
- NULL,
- NULL
-};
-
const pn_io_layer_t ssl_layer = {
process_input_ssl,
process_output_ssl,
@@ -404,12 +394,6 @@ int pn_ssl_init(pn_ssl_t *ssl0, pn_ssl_domain_t *domain, const char *session_id)
ssl->domain = domain;
domain->ref_count++;
- if (domain->allow_unsecured) {
- transport->io_layers[PN_IO_SSL] = &unknown_layer;
- }
- else {
- transport->io_layers[PN_IO_SSL] = &ssl_layer;
- }
if (session_id && domain->mode == PN_SSL_MODE_CLIENT)
ssl->session_id = pn_strdup(session_id);
@@ -439,6 +423,11 @@ int pn_ssl_domain_allow_unsecured_client(pn_ssl_domain_t *domain)
}
+bool pn_ssl_allow_unsecured(pn_transport_t *transport)
+{
+ return transport && transport->ssl && transport->ssl->domain && transport->ssl->domain->allow_unsecured;
+}
+
bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *buffer, size_t size )
{
*buffer = '\0';
@@ -893,12 +882,6 @@ static void start_ssl_shutdown(pn_transport_t *transport)
ssl_handshake(transport);
}
-static int setup_ssl_connection(pn_transport_t *transport, unsigned int layer)
-{
- transport->io_layers[layer] = &ssl_layer;
- return 0;
-}
-
static void rewind_sc_inbuf(pni_ssl_t *ssl)
{
// Decrypted bytes have been drained or double buffered. Prepare for the next SSL Record.
@@ -1270,92 +1253,6 @@ static ssize_t process_output_ssl( pn_transport_t *transport, unsigned int layer
}
-static int setup_cleartext_connection(pn_transport_t *transport, unsigned int layer)
-{
- transport->io_layers[layer] = &pni_passthru_layer;
- return 0;
-}
-
-
-// until we determine if the client is using SSL or not:
-
-static ssize_t process_input_unknown(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len)
-{
- pn_ssl_t *ssl = transport->ssl;
- switch (check_for_ssl_connection( input_data, len )) {
- case SSL_CONNECTION:
- ssl_log(ssl, "SSL connection detected.\n");
- setup_ssl_connection(transport, layer);
- break;
- case CLEAR_CONNECTION:
- ssl_log(ssl, "Cleartext connection detected.\n");
- setup_cleartext_connection(transport, layer);
- break;
- default:
- return 0;
- }
- return transport->io_layers[layer]->process_input(transport, layer, input_data, len);
-}
-
-static ssize_t process_output_unknown(pn_transport_t *transport, unsigned int layer, char *input_data, size_t len)
-{
- // do not do output until we know if SSL is used or not
- return 0;
-}
-
-static connection_mode_t check_for_ssl_connection( const char *data, size_t len )
-{
- if (len >= 5) {
- const unsigned char *buf = (unsigned char *)data;
- /*
- * SSLv2 Client Hello format
- * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
- *
- * Bytes 0-1: RECORD-LENGTH
- * Byte 2: MSG-CLIENT-HELLO (1)
- * Byte 3: CLIENT-VERSION-MSB
- * Byte 4: CLIENT-VERSION-LSB
- *
- * Allowed versions:
- * 2.0 - SSLv2
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- *
- * The version sent in the Client-Hello is the latest version supported by
- * the client. NSS may send version 3.x in an SSLv2 header for
- * maximum compatibility.
- */
- int isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
- ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
- (buf[3] == 2 && buf[4] == 0)); // SSL 2
-
- /*
- * SSLv3/TLS Client Hello format
- * RFC 2246
- *
- * Byte 0: ContentType (handshake - 22)
- * Bytes 1-2: ProtocolVersion {major, minor}
- *
- * Allowed versions:
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- */
- int isSSL3Handshake = buf[0] == 22 && // handshake
- (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
-
- if (isSSL2Handshake || isSSL3Handshake) {
- return SSL_CONNECTION;
- } else {
- return CLEAR_CONNECTION;
- }
- }
- return UNKNOWN_CONNECTION;
-}
-
static ssize_t process_input_done(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len)
{
return PN_EOS;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org