/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. * * This package 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, see . */ #include #include #include "synergy/ProtocolUtil.h" #include "io/IStream.h" #include "base/Log.h" #include "common/stdvector.h" #include #include // // ProtocolUtil // namespace { void writeInt(UInt32 Value, UInt32 Length, std::vector& Buffer) { switch(Length) { case 1: Buffer.push_back(static_cast(Value & 0xffU)); break; case 4: Buffer.push_back(static_cast((Value >> 24U) & 0xffU)); Buffer.push_back(static_cast((Value >> 16U) & 0xffU)); Buffer.push_back(static_cast((Value >> 8U) & 0xffU)); Buffer.push_back(static_cast( Value & 0xffU)); break; case 2: Buffer.push_back(static_cast((Value >> 8U) & 0xffU)); Buffer.push_back(static_cast( Value & 0xffU)); break; default: assert(0 && "invalid integer format length"); return; } } template void writeVectorInt(const std::vector* VectorData, std::vector& Buffer) { if (VectorData) { const std::vector& Vector = *VectorData; writeInt((UInt32)Vector.size(), sizeof(UInt32), Buffer); for (size_t i = 0; i < Vector.size(); ++i) { writeInt(Vector[i], sizeof(T), Buffer); } } } void writeString(const String* StringData, std::vector& Buffer) { const UInt32 len = (StringData != NULL) ? (UInt32)StringData->size() : 0; writeInt(len, sizeof(len), Buffer); if (len != 0) { std::copy(StringData->begin(), StringData->end(), std::back_inserter(Buffer)); } } } //namespace void ProtocolUtil::writef(synergy::IStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); LOG((CLOG_DEBUG2 "writef(%s)", fmt)); va_list args; va_start(args, fmt); UInt32 size = getLength(fmt, args); va_end(args); va_start(args, fmt); vwritef(stream, fmt, size, args); va_end(args); } bool ProtocolUtil::readf(synergy::IStream* stream, const char* fmt, ...) { bool result = false; if (stream && fmt) { LOG((CLOG_DEBUG2 "readf(%s)", fmt)); va_list args; va_start(args, fmt); try { vreadf(stream, fmt, args); result = true; } catch (XIO&) { result = false; } catch (const std::bad_alloc&) { result = false; } va_end(args); } return result; } void ProtocolUtil::vwritef(synergy::IStream* stream, const char* fmt, UInt32 size, va_list args) { assert(stream != NULL); assert(fmt != NULL); // done if nothing to write if (size == 0) { return; } // fill buffer std::vector Buffer; writef(Buffer, fmt, args); try { // write buffer stream->write(Buffer.data(), size); LOG((CLOG_DEBUG2 "wrote %d bytes", size)); } catch (const XBase& exception) { LOG((CLOG_DEBUG2 "Exception <%s> during wrote %d bytes into stream", exception.what(), size)); throw; } } void ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) { assert(stream != NULL); assert(fmt != NULL); // begin scanning while (*fmt) { if (*fmt == '%') { // format specifier. determine argument size. ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { case 'i': { void* destination = va_arg(args, void*); switch (len) { case 1: // 1 byte integer *static_cast(destination) = read1ByteInt(stream); break; case 2: // 2 byte integer *static_cast(destination) = read2BytesInt(stream); break; case 4: // 4 byte integer *static_cast(destination) = read4BytesInt(stream); break; default: //the length is wrong LOG((CLOG_ERR "read: length to be read is wrong: '%d' should be 1,2, or 4", len)); assert(false); //assert for debugging break; } break; } case 'I': { void* destination = va_arg(args, void*); switch (len) { case 1: // 1 byte integer readVector1ByteInt(stream, *static_cast*>(destination)); break; case 2: // 2 byte integer readVector2BytesInt(stream, *static_cast*>(destination)); break; case 4: // 4 byte integer readVector4BytesInt(stream, *static_cast*>(destination)); break; default: //the length is wrong LOG((CLOG_ERR "read: length to be read is wrong: '%d' should be 1,2, or 4", len)); assert(false); //assert for debugging break; } break; } case 's': { String* destination = va_arg(args, String*); readBytes(stream, len, destination); break; } case '%': assert(len == 0); break; default: assert(0 && "invalid format specifier"); } // next format character ++fmt; } else { // read next character char buffer[1]; read(stream, buffer, 1); // verify match if (buffer[0] != *fmt) { LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); throw XIOReadMismatch(); } // next format character ++fmt; } } } UInt32 ProtocolUtil::getLength(const char* fmt, va_list args) { UInt32 n = 0; while (*fmt) { if (*fmt == '%') { // format specifier. determine argument size. ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { case 'i': assert(len == 1 || len == 2 || len == 4); (void)va_arg(args, UInt32); break; case 'I': assert(len == 1 || len == 2 || len == 4); switch (len) { case 1: len = (UInt32)(va_arg(args, std::vector*))->size() + 4; break; case 2: len = 2 * (UInt32)(va_arg(args, std::vector*))->size() + 4; break; case 4: len = 4 * (UInt32)(va_arg(args, std::vector*))->size() + 4; break; } break; case 's': assert(len == 0); len = (UInt32)(va_arg(args, String*))->size() + 4; break; case 'S': assert(len == 0); len = va_arg(args, UInt32) + 4; break; case '%': assert(len == 0); len = 1; break; default: assert(0 && "invalid format specifier"); } // accumulate size n += len; ++fmt; } else { // regular character ++n; ++fmt; } } return n; } void ProtocolUtil::writef(std::vector& buffer, const char* fmt, va_list args) { while (*fmt) { if (*fmt == '%') { // format specifier. determine argument size. ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { case 'i': { const UInt32 v = va_arg(args, UInt32); writeInt(v, len, buffer); break; } case 'I': { switch (len) { case 1: { // 1 byte integers const std::vector* list = va_arg(args, const std::vector*); writeVectorInt(list, buffer); break; } case 2: { // 2 byte integers const std::vector* list = va_arg(args, const std::vector*); writeVectorInt(list, buffer); break; } case 4: { // 4 byte integers const std::vector* list = va_arg(args, const std::vector*); writeVectorInt(list, buffer); break; } default: assert(0 && "invalid integer vector format length"); return; } break; } case 's': { assert(len == 0); const String* src = va_arg(args, String*); writeString(src, buffer); break; } case 'S': { assert(len == 0); const UInt32 len = va_arg(args, UInt32); const UInt8* src = va_arg(args, UInt8*); writeInt(len, sizeof(len), buffer); std::copy(src, src + len, std::back_inserter(buffer)); break; } case '%': assert(len == 0); buffer.push_back('%'); break; default: assert(0 && "invalid format specifier"); } // next format character ++fmt; } else { // copy regular character buffer.push_back(*fmt++); } } } UInt32 ProtocolUtil::eatLength(const char** pfmt) { const char* fmt = *pfmt; UInt32 n = 0; for (;;) { UInt32 d; switch (*fmt) { case '0': d = 0; break; case '1': d = 1; break; case '2': d = 2; break; case '3': d = 3; break; case '4': d = 4; break; case '5': d = 5; break; case '6': d = 6; break; case '7': d = 7; break; case '8': d = 8; break; case '9': d = 9; break; default: *pfmt = fmt; return n; } n = 10 * n + d; ++fmt; } } void ProtocolUtil::read(synergy::IStream* stream, void* vbuffer, UInt32 count) { assert(stream != NULL); assert(vbuffer != NULL); UInt8* buffer = static_cast(vbuffer); while (count > 0) { // read more UInt32 n = stream->read(buffer, count); // bail if stream has hungup if (n == 0) { LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); throw XIOEndOfStream(); } // prepare for next read buffer += n; count -= n; } } UInt8 ProtocolUtil::read1ByteInt(synergy::IStream * stream) { const UInt32 BufferSize = 1; std::array buffer = {}; read(stream, buffer.data(), BufferSize); UInt8 Result = buffer[0]; LOG((CLOG_DEBUG2 "readf: read 1 byte integer: %d (0x%x)", Result, Result)); return Result; } UInt16 ProtocolUtil::read2BytesInt(synergy::IStream * stream) { const UInt32 BufferSize = 2; std::array buffer = {}; read(stream, buffer.data(), BufferSize); UInt16 Result = static_cast((static_cast(buffer[0]) << 8) | static_cast(buffer[1])); LOG((CLOG_DEBUG2 "readf: read 2 byte integer: %d (0x%x)", Result, Result)); return Result; } UInt32 ProtocolUtil::read4BytesInt(synergy::IStream * stream) { const int BufferSize = 4; std::array buffer = {}; read(stream, buffer.data(), BufferSize); UInt32 Result = (static_cast(buffer[0]) << 24) | (static_cast(buffer[1]) << 16) | (static_cast(buffer[2]) << 8) | (static_cast(buffer[3])); LOG((CLOG_DEBUG2 "readf: read 4 byte integer: %d (0x%x)", Result, Result)); return Result; } void ProtocolUtil::readVector1ByteInt(synergy::IStream* stream, std::vector& destination) { UInt32 size = read4BytesInt(stream); for (UInt32 i = 0; i < size; ++i) { destination.push_back(read1ByteInt(stream)); } } void ProtocolUtil::readVector2BytesInt(synergy::IStream* stream, std::vector& destination) { UInt32 size = read4BytesInt(stream); for (UInt32 i = 0; i < size; ++i) { destination.push_back(read2BytesInt(stream)); } } void ProtocolUtil::readVector4BytesInt(synergy::IStream* stream, std::vector& destination) { UInt32 size = read4BytesInt(stream); for (UInt32 i = 0; i < size; ++i) { destination.push_back(read4BytesInt(stream)); } } void ProtocolUtil::readBytes(synergy::IStream * stream, UInt32 len, String* destination) { assert(len == 0); // read the string length UInt8 buffer[128]; len = read4BytesInt(stream); // use a fixed size buffer if its big enough const bool useFixed = (len <= sizeof(buffer)); // allocate a buffer to read the data UInt8* sBuffer = buffer; if (!useFixed) { try{ sBuffer = new UInt8[len]; } catch (std::bad_alloc & exception) { // Added try catch due to GHSA-chfm-333q-gfpp LOG((CLOG_ERR "ALLOC: Unable to allocate memory %d bytes", len)); LOG((CLOG_DEBUG "bad_alloc detected: Do you have enough free memory?")); throw exception; } } // read the data try { read(stream, sBuffer, len); } catch (...) { if (!useFixed) { delete[] sBuffer; } throw; } LOG((CLOG_DEBUG2 "readf: read %d byte string", len)); // save the data if (destination){ destination->assign((const char*)sBuffer, len); } // release the buffer if (!useFixed) { delete[] sBuffer; } } // // XIOReadMismatch // String XIOReadMismatch::getWhat() const throw() { return format("XIOReadMismatch", "ProtocolUtil::readf() mismatch"); }