File kdump-sftppacket.patch of Package kdump
From: Petr Tesarik <ptesarik@suse.cz>
Date: Wed, 15 Apr 2015 12:50:49 +0200
Subject: Class to encode/decode SFTP packets
References: FATE#318874, bsc#917747
Patch-mainline: v0.8.16
Git-commit: 6854130651fc82a4055fe49e60be7e70a3458e6d
Git-commit: bb2833a563182ae9e0d3f0dc68d9c49536f61a54
Git-commit: e8a8df2b30120d8130b6007e7b14a489bd61e79b
Git-commit: 149dd520cc1a6419c1ec36a999c80072df74ec74
Git-commit: e7699507d1b2c54452687af22c155222f65a550a
Git-commit: d49008c86d26836ad6fcdc967741bf8dd3970953
Git-commit: 1988f5798476edcde4ad7bb2c577e755ccf610ff
Git-commit: c88dd77c52d591855a9f31217fcc43513d0e6d05
Git-commit: 920b35981bd0dcd4e7ada32d39bbd191f1d63281
Git-commit: 1854f1b84fe2bc95a7339f33ee5db34dc0af0320
Git-commit: 08c884ea6e9d9e0f537035be25cf6c37cc0dc269
Git-commit: 8c18a0bd9b7fefde98333ad97ee4ee4709465357
The sftp protocol is sufficiently flexible to allow saving makedumpfile's
output directly, but the sftp client is not (it has no way to copy standard
input to a target file). This is why kdumptool must implement the necessary
protocol commands itself.
The SFTPPacket class can be used both for creating client requests and for
parsing server replies.
Signed-off-by: Petr Tesarik <ptesarik@suse.cz>
---
kdumptool/CMakeLists.txt | 5 +
kdumptool/sshtransfer.cc | 83 +++++++++++++++++++
kdumptool/sshtransfer.h | 49 +++++++++++
kdumptool/testsftppacket.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++
tests/CMakeLists.txt | 4
tests/testsftppacket.sh | 181 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 511 insertions(+)
--- a/kdumptool/CMakeLists.txt
+++ b/kdumptool/CMakeLists.txt
@@ -149,3 +149,8 @@ add_executable(testurldecode
testurldecode.cc
)
target_link_libraries(testurldecode common ${EXTRA_LIBS})
+
+add_executable(testsftppacket
+ testsftppacket.cc
+)
+target_link_libraries(testsftppacket common ${EXTRA_LIBS})
--- a/kdumptool/sshtransfer.cc
+++ b/kdumptool/sshtransfer.cc
@@ -20,6 +20,7 @@
#include <string>
#include <cstdlib>
#include <cerrno>
+#include <stdint.h>
#if HAVE_LIBSSH2
# include <libssh2.h>
@@ -165,6 +166,88 @@ StringVector SSHTransfer::makeArgs(std::
}
//}}}
+//{{{ SFTPPacket ---------------------------------------------------------------
+
+/* -------------------------------------------------------------------------- */
+SFTPPacket::SFTPPacket(void)
+ : m_vector(sizeof(uint32_t)),
+ m_gpos(0)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+void SFTPPacket::addByteVector(ByteVector const &val)
+{
+ m_vector.insert(m_vector.end(), val.begin(), val.end());
+}
+
+/* -------------------------------------------------------------------------- */
+void SFTPPacket::addInt32(unsigned long val)
+{
+ int i;
+ for (i = sizeof(uint32_t) - 1; i >= 0; --i)
+ m_vector.push_back((val >> (i*8)) & 0xff);
+}
+
+/* -------------------------------------------------------------------------- */
+unsigned long SFTPPacket::getInt32(void)
+{
+ size_t i;
+ unsigned long ret = 0UL;
+ for (i = 0; i < sizeof(uint32_t); ++i) {
+ ret <<= 8;
+ ret |= m_vector.at(m_gpos++);
+ }
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+void SFTPPacket::addInt64(unsigned long long val)
+{
+ int i;
+ for (i = sizeof(uint64_t) - 1; i >= 0; --i)
+ m_vector.push_back((val >> (i*8)) & 0xff);
+}
+
+/* -------------------------------------------------------------------------- */
+unsigned long long SFTPPacket::getInt64(void)
+{
+ size_t i;
+ unsigned long long ret = 0ULL;
+ for (i = 0; i < sizeof(uint64_t); ++i) {
+ ret <<= 8;
+ ret |= m_vector.at(m_gpos++);
+ }
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+void SFTPPacket::addString(KString const &val)
+{
+ addInt32(val.length());
+ m_vector.insert(m_vector.end(), val.begin(), val.end());
+}
+
+/* -------------------------------------------------------------------------- */
+std::string SFTPPacket::getString(void)
+{
+ unsigned long len = getInt32();
+ ByteVector::iterator it = m_vector.begin() + m_gpos;
+ return string(it, it + len);
+}
+
+/* -------------------------------------------------------------------------- */
+ByteVector const &SFTPPacket::update(void)
+{
+ uint_fast32_t len = m_vector.size() - sizeof(uint32_t);
+ m_vector[0] = (len >> 24) & 0xff;
+ m_vector[1] = (len >> 16) & 0xff;
+ m_vector[2] = (len >> 8) & 0xff;
+ m_vector[3] = (len ) & 0xff;
+ return m_vector;
+}
+
+//}}}
//{{{ SFTPTransfer -------------------------------------------------------------
#if HAVE_LIBSSH2
--- a/kdumptool/sshtransfer.h
+++ b/kdumptool/sshtransfer.h
@@ -72,6 +72,55 @@ class SSHTransfer : public URLTransfer {
};
//}}}
+//{{{ SFTPPacket ---------------------------------------------------------------
+
+/**
+ * Encode/decode an SFTP packet.
+ */
+class SFTPPacket {
+
+ public:
+
+ SFTPPacket(void);
+
+ ByteVector const &data(void) const
+ throw ()
+ { return m_vector; }
+
+ ByteVector const &update(void);
+
+ void setData(ByteVector const &val)
+ {
+ m_vector = val;
+ m_gpos = 0;
+ }
+
+ void addByte(unsigned char val)
+ { m_vector.push_back(val); }
+
+ void addByteVector(ByteVector const &val);
+
+ void addInt32(unsigned long val);
+
+ void addInt64(unsigned long long val);
+
+ void addString(KString const &val);
+
+ unsigned char getByte(void)
+ { return m_vector.at(m_gpos++); }
+
+ unsigned long getInt32(void);
+
+ unsigned long long getInt64(void);
+
+ std::string getString(void);
+
+ private:
+ ByteVector m_vector;
+ size_t m_gpos;
+};
+
+//}}}
//{{{ SFTPTransfer -------------------------------------------------------------
#if HAVE_LIBSSH2
--- /dev/null
+++ b/kdumptool/testsftppacket.cc
@@ -0,0 +1,189 @@
+/*
+ * (c) 2015, Petr Tesarik <ptesarik@suse.de>, SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <iostream>
+#include <cstdlib>
+
+#include "global.h"
+#include "stringutil.h"
+#include "sshtransfer.h"
+#include "debug.h"
+
+using std::cout;
+using std::cerr;
+using std::setw;
+using std::endl;
+
+// -----------------------------------------------------------------------------
+static void
+dumpvec(ByteVector const &bv)
+{
+ cout << std::hex << std::setfill('0');
+ ByteVector::const_iterator it = bv.begin();
+ if (it != bv.end()) {
+ cout << setw(2) << unsigned(*it);
+ ++it;
+ }
+ while (it != bv.end()) {
+ cout << ' ' << setw(2) << unsigned(*it);
+ ++it;
+ }
+ cout << endl;
+}
+
+// -----------------------------------------------------------------------------
+static unsigned long long
+parseval(const char *str, unsigned maxdigits)
+{
+ unsigned long long ret = 0;
+ unsigned i;
+
+ for (i = 0; i < maxdigits; ++i) {
+ if (!str[i])
+ break;
+ ret <<= 4;
+ ret |= Stringutil::hex2int(str[i]);
+ }
+ if (str[i])
+ throw KError(KString("Number too big: '") + str + "'");
+
+ return ret;
+}
+
+// -----------------------------------------------------------------------------
+static ByteVector
+parsevec(const char *str)
+{
+ ByteVector ret;
+
+ while(*str) {
+ unsigned char byte;
+ byte = Stringutil::hex2int(*str++);
+ if (*str) {
+ byte <<= 4;
+ byte |= Stringutil::hex2int(*str++);
+ }
+ ret.push_back(byte);
+ }
+ return ret;
+}
+
+// -----------------------------------------------------------------------------
+static void
+getbyte(SFTPPacket &pkt)
+{
+ cout << std::hex << std::setfill('0') << setw(2)
+ << unsigned(pkt.getByte()) << endl;
+}
+
+// -----------------------------------------------------------------------------
+static void
+getint32(SFTPPacket &pkt)
+{
+ cout << std::hex << std::setfill('0') << setw(8)
+ << pkt.getInt32() << endl;
+}
+
+// -----------------------------------------------------------------------------
+static void
+getint64(SFTPPacket &pkt)
+{
+ cout << std::hex << std::setfill('0') << setw(16)
+ << pkt.getInt64() << endl;
+}
+
+// -----------------------------------------------------------------------------
+static void
+getstring(SFTPPacket &pkt)
+{
+ cout << pkt.getString() << endl;
+}
+
+// -----------------------------------------------------------------------------
+int main(int argc, char *argv[])
+{
+ Debug::debug()->setStderrLevel(Debug::DL_TRACE);
+
+ try {
+ SFTPPacket pkt;
+
+ int i;
+ for (i = 1; i < argc; ++i) {
+ char *arg = argv[i];
+
+ switch (arg[0]) {
+ case 'd':
+ if (arg[1])
+ pkt.setData(parsevec(arg + 1));
+ else
+ dumpvec(pkt.data());
+ break;
+
+ case 'u':
+ dumpvec(pkt.update());
+ break;
+
+ case 'b':
+ if (arg[1])
+ pkt.addByte(parseval(arg + 1, 2));
+ else
+ getbyte(pkt);
+ break;
+
+ case 'w':
+ if (arg[1])
+ pkt.addInt32(parseval(arg + 1, 8));
+ else
+ getint32(pkt);
+ break;
+
+ case 'l':
+ if (arg[1])
+ pkt.addInt64(parseval(arg + 1, 16));
+ else
+ getint64(pkt);
+ break;
+
+ case 's':
+ if (arg[1])
+ pkt.addString(arg + 1);
+ else
+ getstring(pkt);
+ break;
+
+ case 'v':
+ if (arg[1])
+ pkt.addByteVector(parsevec(arg + 1));
+ break;
+
+ case '\0':
+ // Ignore empty arguments
+ break;
+
+ default:
+ throw KError("Invalid format specifier");
+ }
+ }
+
+ } catch(const std::exception &ex) {
+ cerr << "Fatal exception: " << ex.what() << endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -64,3 +64,7 @@ ADD_TEST(process
ADD_TEST(urldecode
${CMAKE_CURRENT_SOURCE_DIR}/testurldecode.sh
${CMAKE_BINARY_DIR}/kdumptool/testurldecode)
+
+ADD_TEST(sftppacket
+ ${CMAKE_CURRENT_SOURCE_DIR}/testsftppacket.sh
+ ${CMAKE_BINARY_DIR}/kdumptool/testsftppacket)
--- /dev/null
+++ b/tests/testsftppacket.sh
@@ -0,0 +1,181 @@
+#!/bin/bash
+#
+# (c) 2014, Petr Tesarik <ptesarik@suse.de>, SUSE LINUX Products GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+# Check that results match expectation
+# {{{
+function check()
+{
+ local arg="$1"
+ local expect="$2"
+ local result="$3"
+ if [ "$result" != "$expect" ] ; then
+ echo "failed packet: $arg"
+ echo "Expected:"
+ echo "$expect"
+ echo "Result:"
+ echo "$result"
+ errornumber=$(( errornumber + 1 ))
+ fi
+}
+# }}}
+
+# Add random byte to ARG and EXPECT
+# {{{
+function add_random_byte()
+{
+ local ch=$(( $RANDOM % 256 ))
+ local hex=$( printf "%02x" $ch )
+ ARG="$ARG$hex"
+ EXPECT="$EXPECT $hex"
+}
+# }}}
+
+#
+# Program {{{
+#
+
+TESTPACKET=$1
+HEXDUMP="od -Ax -tx1"
+
+if [ -z "$TESTPACKET" ] ; then
+ echo "Usage: $0 testsftppacket"
+ exit 1
+fi
+
+errornumber=0
+
+# TEST #1: Empty packet
+
+ARG="d"
+EXPECT="00 00 00 00"
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #2: Updated empty packet
+
+ARG="u"
+EXPECT="00 00 00 00"
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #3: Any 8-bit value
+ARG=""
+EXPECT="00 00 01 00"
+i=0
+while [ $i -le 255 ]
+do
+ ARG="$ARG "$(printf "b%02x" $i)
+ EXPECT="$EXPECT "$(printf "%02x" $i)
+ i=$(( $i + 1 ))
+done
+RESULT=$( "$TESTPACKET" $ARG u )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #4: 100 random 32-bit values
+ARG=""
+EXPECT="00 00 01 90" # 4*100 in hex
+i=0
+while [ $i -lt 100 ]
+do
+ ARG="$ARG w"
+ add_random_byte # bits 0-7
+ add_random_byte # bits 8-15
+ add_random_byte # bits 16-23
+ add_random_byte # bits 24-31
+
+ i=$(( $i + 1 ))
+done
+RESULT=$( "$TESTPACKET" $ARG u )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #5: 100 random 64-bit values
+ARG=""
+EXPECT="00 00 03 20" # 8*100 in hex
+i=0
+while [ $i -lt 100 ]
+do
+ ARG="$ARG l"
+ add_random_byte # bits 0-7
+ add_random_byte # bits 8-15
+ add_random_byte # bits 16-23
+ add_random_byte # bits 24-31
+ add_random_byte # bits 32-39
+ add_random_byte # bits 40-47
+ add_random_byte # bits 48-55
+ add_random_byte # bits 56-63
+
+ i=$(( $i + 1 ))
+done
+RESULT=$( "$TESTPACKET" $ARG u )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #6: String
+ARG="sHello, world!"
+EXPECT="00 00 00 11 00 00 00 0d 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21"
+RESULT=$( "$TESTPACKET" "$ARG" u )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #7: Vector
+ARG="v0123456789abcdef u"
+EXPECT="00 00 00 08 01 23 45 67 89 ab cd ef"
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #8: Get byte - note that packet length field is still zero!
+ARG="v0123456789abcdef b b b b b b b b b b b b"
+EXPECT=$(echo -e "00\n00\n00\n00\n01\n23\n45\n67\n89\nab\ncd\nef")
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #9: Get 32-bit integers - note that packet length field is zero!
+ARG="v0123456789abcdef w w w"
+EXPECT=$(echo -e "00000000\n01234567\n89abcdef")
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #10: Get 64-bit integer - note that packet length field is zero!
+ARG="v0123456789abcdef w l"
+EXPECT=$(echo -e "00000000\n0123456789abcdef")
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #11: Get string value
+ARG="sHello! w s"
+EXPECT=$(echo -e "00000000\nHello!")
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #12: Set data - note the bogus packet length
+ARG="dbadf1e1d0123456789abcdef d"
+EXPECT="ba df 1e 1d 01 23 45 67 89 ab cd ef"
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+# TEST #13: Set data - note the updated packet length
+ARG="dbadf1e1d0123456789abcdef u"
+EXPECT="00 00 00 08 01 23 45 67 89 ab cd ef"
+RESULT=$( "$TESTPACKET" $ARG )
+check "$ARG" "$EXPECT" "$RESULT"
+
+exit $errornumber
+
+# }}}
+
+# vim: set sw=4 ts=4 fdm=marker et: :collapseFolds=1: