dechunker: Add handler for Dynamic channels
This commit is contained in:
+180
-2
@@ -57,11 +57,17 @@ struct vc_dechunker
|
|||||||
struct stream *reassembly_s;
|
struct stream *reassembly_s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dyn_dechunker
|
||||||
|
{
|
||||||
|
char name[64];
|
||||||
|
struct stream *reassembly_s;
|
||||||
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
// This is a rather arbitrary figure, but one we are unlikely to
|
// This is a rather arbitrary figure, but one we are unlikely to
|
||||||
// go below. It's a sanity check for vc_dechunker_init()
|
// go below. It's a sanity check for vc_dechunker_init()
|
||||||
E_MAX_CHUNK_SIZE_LOWER_LIMIT = 50
|
E_MAX_VC_CHUNK_SIZE_LOWER_LIMIT = 50
|
||||||
};
|
};
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
struct vc_dechunker *
|
struct vc_dechunker *
|
||||||
@@ -72,7 +78,7 @@ vc_dechunker_init(const char *chan_name, int max_chunk_size)
|
|||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "vc_dechunker_init() called with no channel name");
|
LOG(LOG_LEVEL_ERROR, "vc_dechunker_init() called with no channel name");
|
||||||
}
|
}
|
||||||
else if (max_chunk_size < E_MAX_CHUNK_SIZE_LOWER_LIMIT)
|
else if (max_chunk_size < E_MAX_VC_CHUNK_SIZE_LOWER_LIMIT)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "Dechunker: Max chunk size for %s is too small",
|
LOG(LOG_LEVEL_ERROR, "Dechunker: Max chunk size for %s is too small",
|
||||||
chan_name);
|
chan_name);
|
||||||
@@ -237,6 +243,9 @@ handle_reading_state(struct vc_dechunker *self,
|
|||||||
// Tell the caller the stream is available.
|
// Tell the caller the stream is available.
|
||||||
self->state = E_DATA;
|
self->state = E_DATA;
|
||||||
rv = E_VC_READY;
|
rv = E_VC_READY;
|
||||||
|
LOG_DEVEL(LOG_LEVEL_INFO,
|
||||||
|
"Dechunker: Reassembled PDU of size %d on %s",
|
||||||
|
self->reassembly_s->size, self->name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -369,3 +378,172 @@ vc_dechunker_get_stream(struct vc_dechunker *self)
|
|||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
/*****************************************************************************/
|
||||||
|
struct dyn_dechunker *
|
||||||
|
dyn_dechunker_init(const char *chan_name)
|
||||||
|
{
|
||||||
|
struct dyn_dechunker *self = NULL;
|
||||||
|
if (chan_name == NULL)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"dyn_dechunker_init() called with no channel name");
|
||||||
|
}
|
||||||
|
else if ((self = g_new(struct dyn_dechunker, 1)) == NULL)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "Dechunker: no memory for %s", chan_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strlcpy(self->name, chan_name, sizeof(self->name));
|
||||||
|
self->reassembly_s = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
void
|
||||||
|
dyn_dechunker_free(struct dyn_dechunker *self)
|
||||||
|
{
|
||||||
|
if (self != NULL)
|
||||||
|
{
|
||||||
|
free_stream(self->reassembly_s);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
enum dyn_dechunker_status
|
||||||
|
dyn_dechunker_process_first_chunk(struct dyn_dechunker *self,
|
||||||
|
struct stream *s, int total_size)
|
||||||
|
{
|
||||||
|
enum dyn_dechunker_status status = E_DYN_ERROR;
|
||||||
|
|
||||||
|
int frag_size = s ? s_rem(s) : 0;
|
||||||
|
if (self == NULL || s == NULL)
|
||||||
|
{
|
||||||
|
; // Nothing to be done
|
||||||
|
}
|
||||||
|
else if (total_size <= 1590 || frag_size > total_size)
|
||||||
|
{
|
||||||
|
// See [MS-RDPEDYC] 2.2.3
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"Badly sized DYNVC_DATA_FIRST PDU received on dynamic channel %s",
|
||||||
|
self->name);
|
||||||
|
}
|
||||||
|
else if (self->reassembly_s != NULL)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"unexpected DYNVC_DATA_FIRST received on dynamic channel %s",
|
||||||
|
self->name);
|
||||||
|
}
|
||||||
|
else if (frag_size == total_size)
|
||||||
|
{
|
||||||
|
// This chunk ccontains all the data
|
||||||
|
status = E_DYN_INLINE_CHUNK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
make_stream(self->reassembly_s);
|
||||||
|
if (self->reassembly_s)
|
||||||
|
{
|
||||||
|
init_stream(self->reassembly_s, total_size);
|
||||||
|
}
|
||||||
|
if (self->reassembly_s == NULL || self->reassembly_s->data == NULL)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"Out of memory for dynamic PDU reassembly on %s",
|
||||||
|
self->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out_uint8p(self->reassembly_s, s->p, frag_size);
|
||||||
|
in_uint8s(s, frag_size);
|
||||||
|
status = E_DYN_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
enum dyn_dechunker_status
|
||||||
|
dyn_dechunker_process_data_chunk(struct dyn_dechunker *self,
|
||||||
|
struct stream *s)
|
||||||
|
{
|
||||||
|
enum dyn_dechunker_status rv;
|
||||||
|
|
||||||
|
if (self == NULL || s == NULL)
|
||||||
|
{
|
||||||
|
rv = E_DYN_ERROR;
|
||||||
|
}
|
||||||
|
else if (self->reassembly_s == NULL)
|
||||||
|
{
|
||||||
|
rv = E_DYN_INLINE_CHUNK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int frag_size = s_rem(s);
|
||||||
|
// We're currently reconstructing a data PDU from fragments
|
||||||
|
if (!s_check_rem_out(self-> reassembly_s, frag_size))
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"Oversized DYNVC_DATA when reconstructing PDU on %s",
|
||||||
|
self->name);
|
||||||
|
rv = E_DYN_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out_uint8p(self->reassembly_s, s->p, frag_size);
|
||||||
|
in_uint8s(s, frag_size);
|
||||||
|
|
||||||
|
if (s_rem_out(self->reassembly_s) == 0)
|
||||||
|
{
|
||||||
|
// Finished defragging
|
||||||
|
s_mark_end(self->reassembly_s);
|
||||||
|
self->reassembly_s->p = self->reassembly_s->data;
|
||||||
|
rv = E_DYN_READY;
|
||||||
|
LOG_DEVEL(LOG_LEVEL_INFO,
|
||||||
|
"Dechunker: Reassembled PDU of size %d on %s",
|
||||||
|
self->reassembly_s->size, self->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = E_DYN_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
struct stream *
|
||||||
|
dyn_dechunker_get_stream(struct dyn_dechunker *self)
|
||||||
|
{
|
||||||
|
struct stream *s;
|
||||||
|
const char *name = (self != NULL) ? self->name : "<unknown>";
|
||||||
|
if (self == NULL || self->reassembly_s == NULL ||
|
||||||
|
self->reassembly_s->end == self->reassembly_s->data)
|
||||||
|
{
|
||||||
|
LOG (LOG_LEVEL_ERROR,
|
||||||
|
"Dechunker: get stream called for %s with no data available",
|
||||||
|
name);
|
||||||
|
s = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pass ownership of the stream to the caller
|
||||||
|
s = self->reassembly_s;
|
||||||
|
self->reassembly_s = NULL; // So we don't free it ourselves!
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
int
|
||||||
|
dyn_dechunker_pending(struct dyn_dechunker *self)
|
||||||
|
{
|
||||||
|
return (self != NULL && self->reassembly_s != NULL);
|
||||||
|
}
|
||||||
|
|||||||
+97
-8
@@ -34,11 +34,12 @@
|
|||||||
|
|
||||||
struct stream;
|
struct stream;
|
||||||
|
|
||||||
/* Private type */
|
/* Private types */
|
||||||
struct vc_dechunker;
|
struct vc_dechunker; // static virtual channel dechunker
|
||||||
|
struct dyn_dechunker; // dynamic channel dechunker
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returned from dechunker_process_vc_chunk()
|
* Returned from vc_dechunker_process_chunk()
|
||||||
*/
|
*/
|
||||||
enum vc_dechunker_status
|
enum vc_dechunker_status
|
||||||
{
|
{
|
||||||
@@ -49,7 +50,19 @@ enum vc_dechunker_status
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise a virtual channel dechunker
|
* Returned from dyn_dechunker_process_chunk() and
|
||||||
|
* Returned from dyn_dechunker_process_first_chunk()
|
||||||
|
*/
|
||||||
|
enum dyn_dechunker_status
|
||||||
|
{
|
||||||
|
E_DYN_INLINE_CHUNK = 0, ///< This chunk is complete in itself
|
||||||
|
E_DYN_IN_PROGRESS, ///< The dechunker is processing chunks
|
||||||
|
E_DYN_READY, ///< A dechunked stream is now complete
|
||||||
|
E_DYN_ERROR ///< An error occurred (logged)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise a static virtual channel dechunker
|
||||||
*
|
*
|
||||||
* @param chan_name - Name of channel
|
* @param chan_name - Name of channel
|
||||||
* @param max_chunk_size - Max size of chunks allowed on channel
|
* @param max_chunk_size - Max size of chunks allowed on channel
|
||||||
@@ -59,14 +72,14 @@ struct vc_dechunker *
|
|||||||
vc_dechunker_init(const char *chan_name, int max_chunk_size);
|
vc_dechunker_init(const char *chan_name, int max_chunk_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a virtual channel dechunker
|
* Free a static virtual channel dechunker
|
||||||
* @param self vc dechunker to free
|
* @param self vc dechunker to free
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
vc_dechunker_free(struct vc_dechunker *self);
|
vc_dechunker_free(struct vc_dechunker *self);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a virtual channel chunk
|
* Process a static virtual channel chunk
|
||||||
*
|
*
|
||||||
* @param self dechunker
|
* @param self dechunker
|
||||||
* @param s Stream for chunk, positioned at start of chunk
|
* @param s Stream for chunk, positioned at start of chunk
|
||||||
@@ -84,9 +97,9 @@ enum vc_dechunker_status
|
|||||||
vc_dechunker_process_chunk(struct vc_dechunker *self,
|
vc_dechunker_process_chunk(struct vc_dechunker *self,
|
||||||
struct stream *s, int flags, int total_size);
|
struct stream *s, int flags, int total_size);
|
||||||
/**
|
/**
|
||||||
* Get the stream from a ready dechunker
|
* Get the stream from a ready static virtual dechunker
|
||||||
*
|
*
|
||||||
* @param self virtual channel dechunker
|
* @param self static virtual channel dechunker
|
||||||
* @return input stream containing completed chunk
|
* @return input stream containing completed chunk
|
||||||
*
|
*
|
||||||
* Ownership of the stream passes to the caller
|
* Ownership of the stream passes to the caller
|
||||||
@@ -97,4 +110,80 @@ struct stream *
|
|||||||
vc_dechunker_get_stream(struct vc_dechunker *self);
|
vc_dechunker_get_stream(struct vc_dechunker *self);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise a dynamic virtual channel dechunker
|
||||||
|
*
|
||||||
|
* @param chan_name - Name of channel
|
||||||
|
* @return dyn_dechunker
|
||||||
|
*/
|
||||||
|
struct dyn_dechunker *
|
||||||
|
dyn_dechunker_init(const char *chan_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a dynamic channel dechunker
|
||||||
|
* @param self dynamic dechunker to free
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dyn_dechunker_free(struct dyn_dechunker *self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a dynamic channel DYNVC_DATA_FIRST PDU ([MS-RDPEDYC] 2.2.3.1)
|
||||||
|
*
|
||||||
|
* @param self dechunker
|
||||||
|
* @param s Stream for chunk, positioned at start of data
|
||||||
|
* @param total_size length from DYNVC_DATA_FIRST header
|
||||||
|
* @return status of dechunker
|
||||||
|
*
|
||||||
|
* For PDUs of size > 1590 bytes, but < 1600, it is possible for the
|
||||||
|
* status E_DYN_INLINE_CHUNK to be returned, as the first chunk
|
||||||
|
* is complete in itself. This follows from [MS-RDPEDYC] 1.3.3.2.1 and
|
||||||
|
* 2.2.3.1
|
||||||
|
*
|
||||||
|
* E_DYN_READY will not be returned by this call.
|
||||||
|
*
|
||||||
|
* On error, it is not possible to recover the stream, because of the
|
||||||
|
* impossibility of distinguishing self-contained DATA PDUs, and
|
||||||
|
* DATA PDUs which are part of a larger PDU.
|
||||||
|
*/
|
||||||
|
enum dyn_dechunker_status
|
||||||
|
dyn_dechunker_process_first_chunk(struct dyn_dechunker *self,
|
||||||
|
struct stream *s, int total_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a dynamic channel DYNVC_DATA PDU ([MS-RDPEDYC] 2.2.3.2)
|
||||||
|
*
|
||||||
|
* @param self dechunker
|
||||||
|
* @param s Stream for chunk, positioned at start of data
|
||||||
|
* @return status of dechunker
|
||||||
|
*
|
||||||
|
* On error, it is not possible to recover the stream, because of the
|
||||||
|
* impossibility of distinguishing self-contained DATA PDUs, and
|
||||||
|
* DATA PDUs which are part of a larger PDU.
|
||||||
|
*/
|
||||||
|
enum dyn_dechunker_status
|
||||||
|
dyn_dechunker_process_data_chunk(struct dyn_dechunker *self,
|
||||||
|
struct stream *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the stream from a ready dynamic dechunker
|
||||||
|
*
|
||||||
|
* @param self dynamic channel dechunker
|
||||||
|
* @return input stream containing completed chunk
|
||||||
|
*
|
||||||
|
* Ownership of the stream passes to the caller
|
||||||
|
*
|
||||||
|
* Resets the dechunker state so that further chunks can be processed.
|
||||||
|
*/
|
||||||
|
struct stream *
|
||||||
|
dyn_dechunker_get_stream(struct dyn_dechunker *self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries a dynamic dechunker for pending data
|
||||||
|
*
|
||||||
|
* @param self dynamic channel dechunker
|
||||||
|
* @return != 0 if data is stored in the dechunker
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dyn_dechunker_pending(struct dyn_dechunker *self);
|
||||||
|
|
||||||
#endif // DECHUNKER_H
|
#endif // DECHUNKER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user