Blame SOURCES/bz720543-pcmk-remote_handle_endian_changes_between_client_and_server_and_improve_forward_compatibility.patch

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