/*
* 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");
}