F
Francois Grieu
Hi,
I have system X needing to call procedures on a system Y
acting as server (say, "system Y, please decipher this data
using the key of index n in your keystore and return me
the result, or an error code if the key is not found").
I can exchange variable-size 8-bit-byte blocks between
X (acting as client) and Y (acting as server). My problem
is conversion of parameters and result to/from 8-bit-byte
blocks, for a significant and evolving set of functions,
often with variable-size parameters. Y must be highly
resistant to deliberate injection of mis-formatted requests,
same for X on mis-formatted responses. Speed matters to a
degree since Y may use a PowerPC 603 and X may be a
multi-gigahertz Amd64 CPU connected thru PCIe.
The C code for X and Y must be highly portable across systems.
They do not necessary share the same data type size and
endianness, and some crash on unaligned memory access.
I'm willing to assume CHAR_BIT==8, but not much more.
The current code is rather ad-hoc: for each new function the
parameters and result are serialized on the originating side,
and parsed on the receiving side (wich is the harder part),
with no real method. On the parsing side, we have torrents of
unsigned char* inData;
long vIdx = inData[4]<<8 | inData[5];
(sometime the index is an enum) or
long vIdx ;
vIdx = inData[j++]<<8;
vIdx |= inData[j++];
(I'm not making the use of "long" for 2-byte index)
and this is typically interleaved with error checking code
(although optimizing for error cases is pointless).
This is error prone, in particular it is hard to avoid parsing
beyond the end of the received data (I have spotted cases where
the test for length is off-by-one, especially with the second
parsing method) and not introduce practically untestable
dependencies on basic type size.
I have the feeling (did not benchmark) that calling a parsing
function for each argument would incur a significant speed penalty
(especially if we want to avoid globals, which is desirable since
Y is multi-threaded with no support for thread-local globals)
and I do not want to depend on if the compiler for Y supports
inline functions. Thus I'm leaning towards
- a general parsing function parsing all the arguments into
a struct, returning a pointer for variable-size or long
arguments, and doing that according to a description of
the expected input and/or struct interpreted at runtime
- some clever (but readable and robust) use of macros
- some simple C code generating scheme for the serialization
and de-serialization.
Any idea/reference/pointer?
Francois Grieu
I have system X needing to call procedures on a system Y
acting as server (say, "system Y, please decipher this data
using the key of index n in your keystore and return me
the result, or an error code if the key is not found").
I can exchange variable-size 8-bit-byte blocks between
X (acting as client) and Y (acting as server). My problem
is conversion of parameters and result to/from 8-bit-byte
blocks, for a significant and evolving set of functions,
often with variable-size parameters. Y must be highly
resistant to deliberate injection of mis-formatted requests,
same for X on mis-formatted responses. Speed matters to a
degree since Y may use a PowerPC 603 and X may be a
multi-gigahertz Amd64 CPU connected thru PCIe.
The C code for X and Y must be highly portable across systems.
They do not necessary share the same data type size and
endianness, and some crash on unaligned memory access.
I'm willing to assume CHAR_BIT==8, but not much more.
The current code is rather ad-hoc: for each new function the
parameters and result are serialized on the originating side,
and parsed on the receiving side (wich is the harder part),
with no real method. On the parsing side, we have torrents of
unsigned char* inData;
long vIdx = inData[4]<<8 | inData[5];
(sometime the index is an enum) or
long vIdx ;
vIdx = inData[j++]<<8;
vIdx |= inData[j++];
(I'm not making the use of "long" for 2-byte index)
and this is typically interleaved with error checking code
(although optimizing for error cases is pointless).
This is error prone, in particular it is hard to avoid parsing
beyond the end of the received data (I have spotted cases where
the test for length is off-by-one, especially with the second
parsing method) and not introduce practically untestable
dependencies on basic type size.
I have the feeling (did not benchmark) that calling a parsing
function for each argument would incur a significant speed penalty
(especially if we want to avoid globals, which is desirable since
Y is multi-threaded with no support for thread-local globals)
and I do not want to depend on if the compiler for Y supports
inline functions. Thus I'm leaning towards
- a general parsing function parsing all the arguments into
a struct, returning a pointer for variable-size or long
arguments, and doing that according to a description of
the expected input and/or struct interpreted at runtime
- some clever (but readable and robust) use of macros
- some simple C code generating scheme for the serialization
and de-serialization.
Any idea/reference/pointer?
Francois Grieu