Blob Blame History Raw
commit d0cd173e2b9d26a3af6e28746003453500f8f655
Author: Andrew Beekhof <andrew@beekhof.net>
Date:   Thu Oct 31 08:18:11 2013 +1100

    Fix: remote: Handle endian changes between client and server and improve forward compatibility
    
    (cherry picked from commit 0c8cb451bb31335e242da328c8a537ab60095a0f)

diff --git a/configure.ac b/configure.ac
index cfc1b1f..b94c26e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -727,6 +727,7 @@ AC_CHECK_HEADERS(glib.h)
 AC_CHECK_HEADERS(grp.h)
 AC_CHECK_HEADERS(limits.h)
 AC_CHECK_HEADERS(linux/errqueue.h)
+AC_CHECK_HEADERS(linux/swab.h)
 AC_CHECK_HEADERS(malloc.h)
 AC_CHECK_HEADERS(netdb.h)
 AC_CHECK_HEADERS(netinet/in.h)
diff --git a/lib/common/remote.c b/lib/common/remote.c
index c991761..47f0205 100644
--- a/lib/common/remote.c
+++ b/lib/common/remote.c
@@ -43,9 +43,7 @@
 #ifdef HAVE_GNUTLS_GNUTLS_H
 #  undef KEYFILE
 #  include <gnutls/gnutls.h>
-#endif
 
-#ifdef HAVE_GNUTLS_GNUTLS_H
 const int psk_tls_kx_order[] = {
     GNUTLS_KX_DHE_PSK,
     GNUTLS_KX_PSK,
@@ -58,17 +56,87 @@ const int anon_tls_kx_order[] = {
     GNUTLS_KX_RSA,
     0
 };
+#endif
+
+/* Swab macros from linux/swab.h */
+#ifdef HAVE_LINUX_SWAB_H
+#  include <linux/swab.h>
+#else
+/*
+ * casts are necessary for constants, because we never know how for sure
+ * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way.
+ */
+#define ___swab16(x) ((__u16)(                                  \
+        (((__u16)(x) & (__u16)0x00ffU) << 8) |                  \
+        (((__u16)(x) & (__u16)0xff00U) >> 8)))
+
+#define ___swab32(x) ((__u32)(                                  \
+        (((__u32)(x) & (__u32)0x000000ffUL) << 24) |            \
+        (((__u32)(x) & (__u32)0x0000ff00UL) <<  8) |            \
+        (((__u32)(x) & (__u32)0x00ff0000UL) >>  8) |            \
+        (((__u32)(x) & (__u32)0xff000000UL) >> 24)))
+
+#define ___swab64(x) ((__u64)(                                  \
+        (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) |   \
+        (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) |   \
+        (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) |   \
+        (((__u64)(x) & (__u64)0x00000000ff000000ULL) <<  8) |   \
+        (((__u64)(x) & (__u64)0x000000ff00000000ULL) >>  8) |   \
+        (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) |   \
+        (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) |   \
+        (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56)))
+#endif
+
+#define REMOTE_MSG_VERSION 1
+#define ENDIAN_LOCAL 0xBADADBBD
 
 struct crm_remote_header_v0 
 {
+    uint32_t endian;    /* Detect messages from hosts with different endian-ness */
+    uint32_t version;
     uint64_t id;
     uint64_t flags;
-    uint32_t error;
-    uint32_t version;
     uint32_t size_total;
-    uint32_t payload_uncompressed;
+    uint32_t payload_offset;
     uint32_t payload_compressed;
-};
+    uint32_t payload_uncompressed;
+
+        /* New fields get added here */
+
+} __attribute__ ((packed));
+
+static struct crm_remote_header_v0 *
+crm_remote_header(crm_remote_t * remote)
+{
+    struct crm_remote_header_v0 *header = (struct crm_remote_header_v0 *)remote->buffer;
+    if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) {
+        return NULL;
+
+    } else if(header->endian != ENDIAN_LOCAL) {
+        uint32_t endian = __swab32(header->endian);
+
+        CRM_LOG_ASSERT(endian == ENDIAN_LOCAL);
+        if(endian != ENDIAN_LOCAL) {
+            crm_err("Invalid message detected, endian mismatch: %lx is neither %lx nor the swab'd %lx",
+                    ENDIAN_LOCAL, header->endian, endian);
+            return NULL;
+        }
+
+        header->id = __swab64(header->id);
+        header->flags = __swab64(header->flags);
+        header->endian = __swab32(header->endian);
+
+        header->version = __swab32(header->version);
+        header->size_total = __swab32(header->size_total);
+        header->payload_offset = __swab32(header->payload_offset);
+        header->payload_compressed = __swab32(header->payload_compressed);
+        header->payload_uncompressed = __swab32(header->payload_uncompressed);
+    }
+
+    return header;
+}
+
+#ifdef HAVE_GNUTLS_GNUTLS_H
 
 int
 crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms)
@@ -253,13 +321,11 @@ crm_remote_sendv(crm_remote_t * remote, struct iovec * iov, int iovs)
     return rc;
 }
 
-#define PCMK_TLS_VERSION 1
-
 int
 crm_remote_send(crm_remote_t * remote, xmlNode * msg)
 {
-    static uint64_t id = 0;
     int rc = -1;
+    static uint64_t id = 0;
     char *xml_text = dump_xml_unformatted(msg);
 
     struct iovec iov[2];
@@ -278,19 +344,25 @@ crm_remote_send(crm_remote_t * remote, xmlNode * msg)
 
     id++;
     header->id = id;
-    header->version = PCMK_TLS_VERSION;
-    header->size_total = iov[0].iov_len + iov[1].iov_len;
+    header->endian = ENDIAN_LOCAL;
+    header->version = REMOTE_MSG_VERSION;
+    header->payload_offset = iov[0].iov_len;
     header->payload_uncompressed = iov[1].iov_len;
+    header->size_total = iov[0].iov_len + iov[1].iov_len;
 
+    crm_trace("Sending len[0]=%d, start=%x\n",
+              (int)iov[0].iov_len, *(int*)xml_text);
     rc = crm_remote_sendv(remote, iov, 2);
     if (rc < 0) {
         crm_err("Failed to send remote msg, rc = %d", rc);
     }
 
-    free(xml_text);
+    free(iov[0].iov_base);
+    free(iov[1].iov_base);
     return rc;
 }
 
+
 /*!
  * \internal
  * \brief handles the recv buffer and parsing out msgs.
@@ -300,34 +372,35 @@ xmlNode *
 crm_remote_parse_buffer(crm_remote_t * remote)
 {
     xmlNode *xml = NULL;
-    size_t offset = sizeof(struct crm_remote_header_v0);
-    struct crm_remote_header_v0 *header = NULL;
+    struct crm_remote_header_v0 *header = crm_remote_header(remote);
 
-    if (remote->buffer == NULL) {
-        return NULL;
-
-    } else if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) {
+    if (remote->buffer == NULL || header == NULL) {
         return NULL;
     }
 
     /* take ownership of the buffer */
     remote->buffer_offset = 0;
 
-    header = (struct crm_remote_header_v0 *)remote->buffer;
     /* Support compression on the receiving end now, in case we ever want to add it later */
     if (header->payload_compressed) {
         int rc = 0;
         unsigned int size_u = 1 + header->payload_uncompressed;
-        char *uncompressed = calloc(1, offset + size_u);
+        char *uncompressed = calloc(1, header->payload_offset + size_u);
 
         crm_trace("Decompressing message data %d bytes into %d bytes",
                  header->payload_compressed, size_u);
 
-        rc = BZ2_bzBuffToBuffDecompress(uncompressed + offset, &size_u,
-                                        remote->buffer + offset,
+        rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, &size_u,
+                                        remote->buffer + header->payload_offset,
                                         header->payload_compressed, 1, 0);
 
-        if (rc != BZ_OK) {
+        if (rc != BZ_OK && header->version > REMOTE_MSG_VERSION) {
+            crm_warn("Couldn't decompress v%d message, we only understand v%d",
+                     header->version, REMOTE_MSG_VERSION);
+            free(uncompressed);
+            return NULL;
+
+        } else if (rc != BZ_OK) {
             crm_err("Decompression failed: %s (%d)", bz2_strerror(rc), rc);
             free(uncompressed);
             return NULL;
@@ -335,19 +408,23 @@ crm_remote_parse_buffer(crm_remote_t * remote)
 
         CRM_ASSERT(size_u == header->payload_uncompressed);
 
-        memcpy(uncompressed, remote->buffer, offset);       /* Preserve the header */
-        header = (struct crm_remote_header_v0 *)uncompressed;
+        memcpy(uncompressed, remote->buffer, header->payload_offset);       /* Preserve the header */
+        remote->buffer_size = header->payload_offset + size_u;
 
         free(remote->buffer);
-        remote->buffer_size = offset + size_u;
         remote->buffer = uncompressed;
+        header = crm_remote_header(remote);
     }
 
-    CRM_ASSERT(remote->buffer[sizeof(struct crm_remote_header_v0) + header->payload_uncompressed - 1] == 0);
+    CRM_LOG_ASSERT(remote->buffer[sizeof(struct crm_remote_header_v0) + header->payload_uncompressed - 1] == 0);
+
+    xml = string2xml(remote->buffer + header->payload_offset);
+    if (xml == NULL && header->version > REMOTE_MSG_VERSION) {
+        crm_warn("Couldn't parse v%d message, we only understand v%d",
+                 header->version, REMOTE_MSG_VERSION);
 
-    xml = string2xml(remote->buffer + offset);
-    if (xml == NULL) {
-        crm_err("Couldn't parse: '%.120s'", remote->buffer + offset);
+    } else if (xml == NULL) {
+        crm_err("Couldn't parse: '%.120s'", remote->buffer + header->payload_offset);
     }
 
     return xml;
@@ -424,16 +501,16 @@ crm_remote_recv_once(crm_remote_t * remote)
 {
     int rc = 0;
     size_t read_len = sizeof(struct crm_remote_header_v0);
+    struct crm_remote_header_v0 *header = crm_remote_header(remote);
 
-    if(remote->buffer_offset >= sizeof(struct crm_remote_header_v0)) {
-        struct crm_remote_header_v0 *hdr = (struct crm_remote_header_v0 *)remote->buffer;
-
-        read_len = hdr->size_total;
+    if(header) {
+        /* Stop at the end of the current message */
+        read_len = header->size_total;
     }
 
     /* automatically grow the buffer when needed */
     if(remote->buffer_size < read_len) {
-        remote->buffer_size = 2 * read_len;
+           remote->buffer_size = 2 * read_len;
         crm_trace("Expanding buffer to %u bytes", remote->buffer_size);
 
         remote->buffer = realloc(remote->buffer, remote->buffer_size + 1);
@@ -488,23 +565,18 @@ crm_remote_recv_once(crm_remote_t * remote)
         return -ENOTCONN;
     }
 
-    if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) {
-        crm_trace("Not enough data to fill header: %u < %u bytes",
-                  remote->buffer_offset, sizeof(struct crm_remote_header_v0));
-        return -EAGAIN;
-
-    } else {
-        struct crm_remote_header_v0 *hdr = (struct crm_remote_header_v0 *)remote->buffer;
-
-        if(remote->buffer_offset < hdr->size_total) {
+    header = crm_remote_header(remote);
+    if(header) {
+        if(remote->buffer_offset < header->size_total) {
             crm_trace("Read less than the advertised length: %u < %u bytes",
-                      remote->buffer_offset, hdr->size_total);
-            return -EAGAIN;
+                      remote->buffer_offset, header->size_total);
+        } else {
+            crm_trace("Read full message of %u bytes", remote->buffer_offset);
+            return remote->buffer_offset;
         }
     }
 
-    crm_trace("Read full message of %u bytes", remote->buffer_offset);
-    return remote->buffer_offset;
+    return -EAGAIN;
 }
 
 /*!
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index d037022..d99d7c8 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -414,9 +414,6 @@ lrmd_poll(lrmd_t * lrmd, int timeout)
         case CRM_CLIENT_TLS:
             if (native->pending_notify) {
                 return 1;
-            } else if (native->remote->buffer
-                       && strstr(native->remote->buffer, REMOTE_MSG_TERMINATOR)) {
-                return 1;
             }
 
             return crm_remote_ready(native->remote, 0);