mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-01-08 14:36:22 +01:00
651e7a5135
This reverts commit 5a7d2ae84f830242486301cad0b26168f3ec973b.
920 lines
23 KiB
C
920 lines
23 KiB
C
#include "common.h"
|
|
|
|
// List insertion and removal
|
|
VOID channel_add_list_entry(Channel *channel);
|
|
VOID channel_remove_list_entry(Channel *channel);
|
|
|
|
// Generic buffer manipulation routines
|
|
VOID channel_set_buffer_io_handler(ChannelBuffer *buffer, LPVOID context,
|
|
DirectIoHandler dio);
|
|
VOID channel_write_buffer(Channel *channel, ChannelBuffer *buffer,
|
|
PUCHAR chunk, ULONG chunkLength, PULONG bytesWritten);
|
|
VOID channel_read_buffer(Channel *channel, ChannelBuffer *buffer,
|
|
PUCHAR chunk, ULONG chunkLength, PULONG bytesRead);
|
|
|
|
// Channel routine duplication
|
|
ChannelCompletionRoutine *channel_duplicate_completion_routine(
|
|
ChannelCompletionRoutine *in);
|
|
|
|
// Linked list of allocated channels
|
|
Channel *channelList = NULL;
|
|
DWORD channelIdPool = 0;
|
|
|
|
/*
|
|
* Create a new channel, optionally with a supplied identifier.
|
|
*
|
|
* If the identifier is zero, a new unique identifier is allocated.
|
|
*
|
|
* TODO: identifier conflicts due to being able to supply an id
|
|
*/
|
|
Channel *channel_create(DWORD identifier, DWORD flags)
|
|
{
|
|
Channel *channel = NULL;
|
|
|
|
do
|
|
{
|
|
// Allocate storage for the channel
|
|
if (!(channel = (Channel *)malloc(sizeof(Channel))))
|
|
break;
|
|
|
|
// Zero it
|
|
memset(channel, 0, sizeof(Channel));
|
|
|
|
// Set the channel's unique identifier
|
|
channel->identifier = (!identifier) ? ++channelIdPool : identifier;
|
|
channel->interactive = FALSE;
|
|
channel->flags = flags;
|
|
channel->cls = CHANNEL_CLASS_BUFFERED;
|
|
channel->lock = lock_create();
|
|
|
|
memset(&channel->ops, 0, sizeof(channel->ops));
|
|
|
|
// Initialize the channel's buffered default IO handler
|
|
// to the internal buffering methods
|
|
channel_set_buffered_io_handler(channel, &channel->ops.buffered,
|
|
channel_default_io_handler);
|
|
|
|
// Insert the channel into the list of channels
|
|
channel_add_list_entry(channel);
|
|
|
|
} while (0);
|
|
|
|
return channel;
|
|
}
|
|
|
|
/*
|
|
* Creates a stream-based channel with initialized operations. An example of
|
|
* streaming channel is TCP.
|
|
*/
|
|
LINKAGE Channel *channel_create_stream(DWORD identifier,
|
|
DWORD flags, StreamChannelOps *ops)
|
|
{
|
|
Channel *channel = channel_create(identifier, flags);
|
|
|
|
if (channel)
|
|
{
|
|
channel->cls = CHANNEL_CLASS_STREAM;
|
|
|
|
if (ops)
|
|
memcpy(&channel->ops.stream, ops, sizeof(StreamChannelOps));
|
|
else
|
|
memset(&channel->ops, 0, sizeof(channel->ops));
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
/*
|
|
* Creates a datagram-based channel with initialized operations. An example of
|
|
* a datagram channel is UDP.
|
|
*/
|
|
LINKAGE Channel *channel_create_datagram(DWORD identifier,
|
|
DWORD flags, DatagramChannelOps *ops)
|
|
{
|
|
Channel *channel = channel_create(identifier, flags);
|
|
|
|
if (channel)
|
|
{
|
|
channel->cls = CHANNEL_CLASS_DATAGRAM;
|
|
|
|
if (ops)
|
|
memcpy(&channel->ops.datagram, ops, sizeof(DatagramChannelOps));
|
|
else
|
|
memset(&channel->ops, 0, sizeof(channel->ops));
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
/*
|
|
* Creates a pool-based channel with initialized operations. An example of a
|
|
* pool channel is one that operates on a file.
|
|
*/
|
|
LINKAGE Channel *channel_create_pool(DWORD identifier,
|
|
DWORD flags, PoolChannelOps *ops)
|
|
{
|
|
Channel *channel = channel_create(identifier, flags);
|
|
|
|
if (channel)
|
|
{
|
|
channel->cls = CHANNEL_CLASS_POOL;
|
|
|
|
if (ops)
|
|
memcpy(&channel->ops.pool, ops, sizeof(PoolChannelOps));
|
|
else
|
|
memset(&channel->ops, 0, sizeof(channel->ops));
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
/*
|
|
* Destroy a previously allocated channel
|
|
*/
|
|
VOID channel_destroy(Channel *channel, Packet *request)
|
|
{
|
|
dprintf( "[CHANNEL] channel_destroy. channel=0x%08X", channel );
|
|
// Call the close handler as we're being destroyed.
|
|
if ((channel_get_class(channel) == CHANNEL_CLASS_BUFFERED) &&
|
|
(channel->ops.buffered.dio))
|
|
{
|
|
channel->ops.buffered.dio(channel, &channel->ops.buffered,
|
|
channel->ops.buffered.dioContext, CHANNEL_DIO_MODE_CLOSE,
|
|
NULL, 0, NULL);
|
|
|
|
if (channel->ops.buffered.buffer)
|
|
free(channel->ops.buffered.buffer);
|
|
}
|
|
else
|
|
{
|
|
NativeChannelOps *ops = (NativeChannelOps *)&channel->ops;
|
|
|
|
if (ops->close)
|
|
ops->close(channel, request, ops->context);
|
|
}
|
|
|
|
// Remove the channel from the list of channels
|
|
channel_remove_list_entry(channel);
|
|
|
|
lock_destroy( channel->lock );
|
|
|
|
// Destroy the channel context
|
|
dprintf( "[CHANNEL] Free up the channel context 0x%p", channel );
|
|
free(channel);
|
|
}
|
|
|
|
/*
|
|
* Get the channel's identifier
|
|
*/
|
|
DWORD channel_get_id(Channel *channel)
|
|
{
|
|
return channel->identifier;
|
|
}
|
|
|
|
/*
|
|
* Set the type of channel, such as process, fs, etc.
|
|
*/
|
|
VOID channel_set_type(Channel *channel, PCHAR type)
|
|
{
|
|
if (channel->type)
|
|
free(channel->type);
|
|
|
|
channel->type = NULL;
|
|
|
|
if (type)
|
|
channel->type = _strdup(type);
|
|
}
|
|
|
|
/*
|
|
* Get the channel's type.
|
|
*/
|
|
PCHAR channel_get_type(Channel *channel)
|
|
{
|
|
return channel->type;
|
|
}
|
|
|
|
/*
|
|
* Returns the channel's class
|
|
*/
|
|
DWORD channel_get_class(Channel *channel)
|
|
{
|
|
return channel->cls;
|
|
}
|
|
|
|
/*
|
|
* Sets the channel's operational flags
|
|
*/
|
|
VOID channel_set_flags(Channel *channel, ULONG flags)
|
|
{
|
|
channel->flags = flags;
|
|
}
|
|
|
|
/*
|
|
* Checks to see if the supplied flag is set on the channel
|
|
*/
|
|
BOOLEAN channel_is_flag(Channel *channel, ULONG flag)
|
|
{
|
|
return ((channel->flags & flag) == flag) ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* Returns the channel's operational flags
|
|
*/
|
|
ULONG channel_get_flags(Channel *channel)
|
|
{
|
|
return channel->flags;
|
|
}
|
|
|
|
/*
|
|
* Set the channel's interactive flag
|
|
*/
|
|
VOID channel_set_interactive(Channel *channel, BOOL interactive)
|
|
{
|
|
channel->interactive = interactive;
|
|
}
|
|
|
|
/*
|
|
* Return the channel's interactive flag
|
|
*/
|
|
BOOL channel_is_interactive(Channel *channel)
|
|
{
|
|
return channel->interactive;
|
|
}
|
|
|
|
/*
|
|
* Set the buffered buffer direct IO handler
|
|
*/
|
|
VOID channel_set_buffered_io_handler(Channel *channel, LPVOID dioContext,
|
|
DirectIoHandler dio)
|
|
{
|
|
channel_set_buffer_io_handler(&channel->ops.buffered, dioContext, dio);
|
|
}
|
|
|
|
PVOID channel_get_buffered_io_context(Channel *channel)
|
|
{
|
|
return channel->ops.buffered.dioContext;
|
|
}
|
|
|
|
/*
|
|
* Write the supplied buffer to the remote endpoint of the channel.
|
|
*
|
|
* This will cause the passed buffer to be written in channel->ops.buffered on the
|
|
* remote endpoint.
|
|
*/
|
|
DWORD channel_write_to_remote(Remote *remote, Channel *channel, PUCHAR chunk,
|
|
ULONG chunkLength, PULONG bytesWritten)
|
|
{
|
|
Packet *request = packet_create(PACKET_TLV_TYPE_REQUEST,
|
|
"core_channel_write");
|
|
DWORD res = ERROR_SUCCESS;
|
|
Tlv entries[2];
|
|
DWORD idNbo;
|
|
|
|
do
|
|
{
|
|
// Did the allocation fail?
|
|
if (!request)
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
idNbo = htonl(channel_get_id(channel));
|
|
|
|
entries[0].header.type = TLV_TYPE_CHANNEL_ID;
|
|
entries[0].header.length = sizeof(DWORD);
|
|
entries[0].buffer = (PUCHAR)&idNbo;
|
|
|
|
// if the channel data is ment to be compressed, compress it!
|
|
if( channel_is_flag( channel, CHANNEL_FLAG_COMPRESS ) )
|
|
entries[1].header.type = TLV_TYPE_CHANNEL_DATA|TLV_META_TYPE_COMPRESSED;
|
|
else
|
|
entries[1].header.type = TLV_TYPE_CHANNEL_DATA;
|
|
|
|
entries[1].header.length = chunkLength;
|
|
entries[1].buffer = chunk;
|
|
|
|
// Add the TLV data
|
|
if ((res = packet_add_tlv_group(request, TLV_TYPE_CHANNEL_DATA_GROUP, entries, 2)) != ERROR_SUCCESS)
|
|
break;
|
|
|
|
// Transmit the packet
|
|
res = PACKET_TRANSMIT(remote, request, NULL);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Write data into the buffered buffer using the established DIO operation for
|
|
* writing.
|
|
*/
|
|
DWORD channel_write_to_buffered(Channel *channel, PUCHAR chunk, ULONG chunkLength,
|
|
PULONG bytesWritten)
|
|
{
|
|
return channel->ops.buffered.dio(channel, &channel->ops.buffered,
|
|
channel->ops.buffered.dioContext, CHANNEL_DIO_MODE_WRITE, chunk,
|
|
chunkLength, bytesWritten);
|
|
}
|
|
|
|
/*
|
|
* Read data from the buffered buffer using the established DIO operation for
|
|
* reading.
|
|
*/
|
|
DWORD channel_read_from_buffered(Channel *channel, PUCHAR chunk, ULONG chunkLength,
|
|
PULONG bytesRead)
|
|
{
|
|
return channel->ops.buffered.dio(channel, &channel->ops.buffered,
|
|
channel->ops.buffered.dioContext, CHANNEL_DIO_MODE_READ, chunk, chunkLength,
|
|
bytesRead);
|
|
}
|
|
|
|
/*
|
|
* Sets a given buffer's direct IO handler
|
|
*/
|
|
VOID channel_set_buffer_io_handler(ChannelBuffer *buffer, LPVOID context,
|
|
DirectIoHandler dio)
|
|
{
|
|
// If no direct I/O handler is supplied, use the default
|
|
if (!dio)
|
|
{
|
|
dio = channel_default_io_handler;
|
|
context = NULL;
|
|
}
|
|
|
|
buffer->dioContext = context;
|
|
buffer->dio = dio;
|
|
}
|
|
|
|
/*
|
|
* Changes the native I/O context for the supplied channel
|
|
*/
|
|
LINKAGE VOID channel_set_native_io_context(Channel *channel, LPVOID context)
|
|
{
|
|
NativeChannelOps *ops = (NativeChannelOps *)&channel->ops;
|
|
|
|
ops->context = context;
|
|
}
|
|
|
|
/*
|
|
* Returns the native context that is associated with the channel
|
|
*/
|
|
LINKAGE LPVOID channel_get_native_io_context(Channel *channel)
|
|
{
|
|
NativeChannelOps *ops = (NativeChannelOps *)&channel->ops;
|
|
|
|
return ops->context;
|
|
}
|
|
|
|
/**********************
|
|
* Remote channel API *
|
|
**********************/
|
|
|
|
/*
|
|
* Duplicates a completion routine so it can be saved for calling back
|
|
*/
|
|
ChannelCompletionRoutine *channel_duplicate_completion_routine(
|
|
ChannelCompletionRoutine *in)
|
|
{
|
|
ChannelCompletionRoutine *ret = NULL;
|
|
|
|
if ((ret = (ChannelCompletionRoutine *)malloc(
|
|
sizeof(ChannelCompletionRoutine))))
|
|
memcpy(ret, in, sizeof(ChannelCompletionRoutine));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Channel completion routine dispatcher
|
|
*/
|
|
DWORD _channel_packet_completion_routine(Remote *remote, Packet *packet,
|
|
LPVOID context, LPCSTR method, DWORD result)
|
|
{
|
|
ChannelCompletionRoutine *comp = (ChannelCompletionRoutine *)context;
|
|
DWORD channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
Channel *channel = channel_find_by_id(channelId);
|
|
DWORD res = ERROR_NOT_FOUND;
|
|
|
|
|
|
dprintf( "[CHANNEL] _channel_packet_completion_routine. channel=0x%08X method=%s", channel, method );
|
|
|
|
// If the channel was not found and it isn't an open request, return failure
|
|
if (!channel && strcmp(method, "core_channel_open"))
|
|
return ERROR_NOT_FOUND;
|
|
|
|
if ((!strcmp(method, "core_channel_open")) &&
|
|
(comp->routine.open))
|
|
res = comp->routine.open(remote, channel, comp->context, result);
|
|
else if ((!strcmp(method, "core_channel_read")) &&
|
|
(comp->routine.read))
|
|
{
|
|
ULONG length = 0, realLength = 0;
|
|
PUCHAR buffer = NULL;
|
|
|
|
// Get the number of bytes written
|
|
length = packet_get_tlv_value_uint(packet, TLV_TYPE_LENGTH);
|
|
|
|
// Allocate storage for it
|
|
if ((length) && (buffer = (PUCHAR)malloc(length)))
|
|
{
|
|
memset(buffer, 0, length);
|
|
|
|
channel_read_from_buffered(channel, buffer, length, &realLength);
|
|
}
|
|
|
|
res = comp->routine.read(remote, channel, comp->context, result,
|
|
buffer, realLength);
|
|
|
|
if (buffer)
|
|
free(buffer);
|
|
}
|
|
else if ((!strcmp(method, "core_channel_write")) &&
|
|
(comp->routine.write))
|
|
{
|
|
Tlv lengthTlv;
|
|
ULONG length = 0;
|
|
|
|
// Get the number of bytes written to the channel
|
|
if ((packet_get_tlv(packet, TLV_TYPE_LENGTH, &lengthTlv)
|
|
== ERROR_SUCCESS) &&
|
|
(lengthTlv.header.length >= sizeof(DWORD)))
|
|
length = ntohl(*(LPDWORD)lengthTlv.buffer);
|
|
|
|
res = comp->routine.write(remote, channel, comp->context, result,
|
|
length);
|
|
}
|
|
else if ((!strcmp(method, "core_channel_close")) &&
|
|
(comp->routine.close)) {
|
|
dprintf( "[CHANNEL] freeing up the completion context" );
|
|
res = comp->routine.close(remote, channel, comp->context, result);
|
|
}
|
|
else if ((!strcmp(method, "core_channel_interact")) &&
|
|
(comp->routine.interact))
|
|
res = comp->routine.interact(remote, channel, comp->context, result);
|
|
|
|
// Deallocate the completion context
|
|
dprintf( "[CHANNEL] freeing up the completion context" );
|
|
free(comp);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Tries to open a channel with the remote endpoint, optionally calling the
|
|
* supplied completion routine upon response.
|
|
*/
|
|
DWORD channel_open(Remote *remote, Tlv *addend, DWORD addendLength, ChannelCompletionRoutine *completionRoutine)
|
|
{
|
|
PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
|
|
ChannelCompletionRoutine *dupe = NULL;
|
|
DWORD res = ERROR_SUCCESS;
|
|
PCHAR method = "core_channel_open";
|
|
Packet *request;
|
|
Tlv methodTlv;
|
|
|
|
do
|
|
{
|
|
// Allocate the request
|
|
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Add the supplied TLVs
|
|
packet_add_tlvs(request, addend, addendLength);
|
|
|
|
// If no method TLV as added, add the default one.
|
|
if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
|
|
{
|
|
packet_add_tlv_string(request, TLV_TYPE_METHOD, method);
|
|
}
|
|
|
|
// Initialize the packet completion routine
|
|
if (completionRoutine)
|
|
{
|
|
// Duplicate the completion routine
|
|
dupe = channel_duplicate_completion_routine(completionRoutine);
|
|
|
|
requestCompletion.context = dupe;
|
|
requestCompletion.routine = _channel_packet_completion_routine;
|
|
realRequestCompletion = &requestCompletion;
|
|
}
|
|
|
|
// Transmit the packet with the supplied completion routine, if any.
|
|
res = PACKET_TRANSMIT(remote, request, realRequestCompletion);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Read data from the remote end of the channel.
|
|
*/
|
|
DWORD channel_read(Channel *channel, Remote *remote, Tlv *addend,
|
|
DWORD addendLength, ULONG length,
|
|
ChannelCompletionRoutine *completionRoutine)
|
|
{
|
|
PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
|
|
ChannelCompletionRoutine *dupe = NULL;
|
|
Packet *request;
|
|
DWORD res = ERROR_SUCCESS;
|
|
PCHAR method = "core_channel_read";
|
|
Tlv methodTlv;
|
|
|
|
do
|
|
{
|
|
// Allocate an empty request
|
|
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Add the supplied TLVs
|
|
packet_add_tlvs(request, addend, addendLength);
|
|
|
|
// If no method TLV as added, add the default one.
|
|
if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
|
|
{
|
|
packet_add_tlv_string(request, TLV_TYPE_METHOD, method);
|
|
}
|
|
|
|
// Add the channel identifier and the length to read
|
|
packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));
|
|
packet_add_tlv_uint(request, TLV_TYPE_LENGTH, length);
|
|
|
|
// Initialize the packet completion routine
|
|
if (completionRoutine)
|
|
{
|
|
// Duplicate the completion routine
|
|
dupe = channel_duplicate_completion_routine(completionRoutine);
|
|
|
|
requestCompletion.context = dupe;
|
|
requestCompletion.routine = _channel_packet_completion_routine;
|
|
realRequestCompletion = &requestCompletion;
|
|
}
|
|
|
|
// Transmit the packet with the supplied completion routine, if any.
|
|
res = PACKET_TRANSMIT(remote, request, realRequestCompletion);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Write to the remote end of the channel
|
|
*/
|
|
DWORD channel_write(Channel *channel, Remote *remote, Tlv *addend,
|
|
DWORD addendLength, PUCHAR buffer, ULONG length,
|
|
ChannelCompletionRoutine *completionRoutine)
|
|
{
|
|
PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
|
|
ChannelCompletionRoutine *dupe = NULL;
|
|
DWORD res = ERROR_SUCCESS;
|
|
LPCSTR method = "core_channel_write";
|
|
Packet *request;
|
|
Tlv methodTlv;
|
|
|
|
do
|
|
{
|
|
// Allocate a request packet
|
|
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Add the supplied TLVs
|
|
packet_add_tlvs(request, addend, addendLength);
|
|
|
|
// If no method TLV as added, add the default one.
|
|
if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
|
|
{
|
|
packet_add_tlv_string(request, TLV_TYPE_METHOD, method);
|
|
}
|
|
|
|
// Add the channel identifier and the length to write
|
|
packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));
|
|
|
|
// if the channel data is ment to be compressed, compress it!
|
|
if (channel_is_flag(channel, CHANNEL_FLAG_COMPRESS))
|
|
{
|
|
packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA | TLV_META_TYPE_COMPRESSED, buffer, length);
|
|
}
|
|
else
|
|
{
|
|
packet_add_tlv_raw(request, TLV_TYPE_CHANNEL_DATA, buffer, length);
|
|
}
|
|
|
|
packet_add_tlv_uint(request, TLV_TYPE_LENGTH, channel_get_id(channel));
|
|
|
|
// Initialize the packet completion routine
|
|
if (completionRoutine)
|
|
{
|
|
// Duplicate the completion routine
|
|
dupe = channel_duplicate_completion_routine(completionRoutine);
|
|
|
|
requestCompletion.context = dupe;
|
|
requestCompletion.routine = _channel_packet_completion_routine;
|
|
realRequestCompletion = &requestCompletion;
|
|
}
|
|
|
|
// Transmit the packet with the supplied completion routine, if any.
|
|
res = PACKET_TRANSMIT(remote, request, realRequestCompletion);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Close the channel provided.
|
|
*/
|
|
DWORD channel_close(Channel *channel, Remote *remote, Tlv *addend,
|
|
DWORD addendLength, ChannelCompletionRoutine *completionRoutine)
|
|
{
|
|
PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
|
|
ChannelCompletionRoutine *dupe = NULL;
|
|
LPCSTR method = "core_channel_close";
|
|
DWORD res = ERROR_SUCCESS;
|
|
Packet *request;
|
|
Tlv methodTlv;
|
|
|
|
do
|
|
{
|
|
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Add the supplied TLVs
|
|
packet_add_tlvs(request, addend, addendLength);
|
|
|
|
// If no method TLV as added, add the default one.
|
|
if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
|
|
{
|
|
packet_add_tlv_string(request, TLV_TYPE_METHOD, method);
|
|
}
|
|
|
|
// Add the channel identifier
|
|
packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));
|
|
|
|
// Initialize the packet completion routine
|
|
if (completionRoutine)
|
|
{
|
|
// Duplicate the completion routine
|
|
dupe = channel_duplicate_completion_routine(completionRoutine);
|
|
|
|
requestCompletion.context = dupe;
|
|
requestCompletion.routine = _channel_packet_completion_routine;
|
|
realRequestCompletion = &requestCompletion;
|
|
}
|
|
|
|
dprintf("[CHANNEL] channel_close. channel=0x%08X completion=0x%.8x", channel, completionRoutine);
|
|
|
|
// Transmit the packet with the supplied completion routine, if any.
|
|
res = PACKET_TRANSMIT(remote, request, realRequestCompletion);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Interact with a given channel such that data on the remote end is
|
|
* forwarded in real time rather than being polled.
|
|
*/
|
|
DWORD channel_interact(Channel *channel, Remote *remote, Tlv *addend,
|
|
DWORD addendLength, BOOL enable,
|
|
ChannelCompletionRoutine *completionRoutine)
|
|
{
|
|
PacketRequestCompletion requestCompletion, *realRequestCompletion = NULL;
|
|
ChannelCompletionRoutine *dupe = NULL;
|
|
LPCSTR method = "core_channel_interact";
|
|
DWORD res = ERROR_SUCCESS;
|
|
Packet *request;
|
|
Tlv methodTlv;
|
|
|
|
do
|
|
{
|
|
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, NULL)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Add the supplied TLVs
|
|
packet_add_tlvs(request, addend, addendLength);
|
|
|
|
// If no method TLV as added, add the default one.
|
|
if (packet_get_tlv(request, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)
|
|
{
|
|
packet_add_tlv_string(request, TLV_TYPE_METHOD, method);
|
|
}
|
|
|
|
// Add the channel identifier
|
|
packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(channel));
|
|
|
|
// Add the enable/disable boolean
|
|
packet_add_tlv_bool(request, TLV_TYPE_BOOL, enable);
|
|
|
|
// Initialize the packet completion routine
|
|
if (completionRoutine)
|
|
{
|
|
// Duplicate the completion routine
|
|
dupe = channel_duplicate_completion_routine(completionRoutine);
|
|
|
|
requestCompletion.context = dupe;
|
|
requestCompletion.routine = _channel_packet_completion_routine;
|
|
realRequestCompletion = &requestCompletion;
|
|
}
|
|
|
|
// Transmit the packet with the supplied completion routine, if any.
|
|
res = PACKET_TRANSMIT(remote, request, realRequestCompletion);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*********************
|
|
* Channel searching *
|
|
*********************/
|
|
|
|
/*
|
|
* Find a channel context by its identifier
|
|
*/
|
|
Channel *channel_find_by_id(DWORD id)
|
|
{
|
|
Channel *current;
|
|
|
|
for (current = channelList; current; current = current->next)
|
|
{
|
|
if (current->identifier == id)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
/*
|
|
* Insert a channel into the channel list
|
|
*/
|
|
VOID channel_add_list_entry(Channel *channel)
|
|
{
|
|
if (channelList)
|
|
{
|
|
channelList->prev = channel;
|
|
}
|
|
|
|
channel->next = channelList;
|
|
channel->prev = NULL;
|
|
channelList = channel;
|
|
}
|
|
|
|
/*
|
|
* Remove a channel from the channel list
|
|
*/
|
|
VOID channel_remove_list_entry(Channel *channel)
|
|
{
|
|
if (channel->prev)
|
|
{
|
|
channel->prev->next = channel->next;
|
|
}
|
|
else
|
|
{
|
|
channelList = channel->next;
|
|
}
|
|
|
|
if (channel->next)
|
|
{
|
|
channel->next->prev = channel->prev;
|
|
}
|
|
}
|
|
|
|
/**************
|
|
* Default IO *
|
|
**************/
|
|
|
|
/*
|
|
* Channel default IO operations
|
|
*
|
|
* The default implementation queues and dequeues write/read operations,
|
|
* respectively.
|
|
*/
|
|
DWORD channel_default_io_handler(Channel *channel, ChannelBuffer *buffer,
|
|
LPVOID context, ChannelDioMode mode, PUCHAR chunk, ULONG length,
|
|
PULONG bytesXfered)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case CHANNEL_DIO_MODE_READ:
|
|
channel_read_buffer(channel, buffer, chunk, length, bytesXfered);
|
|
break;
|
|
case CHANNEL_DIO_MODE_WRITE:
|
|
channel_write_buffer(channel, buffer, chunk, length, bytesXfered);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Writes arbitrary data into a buffer, optionally allocating more memory
|
|
* as necessary.
|
|
*/
|
|
VOID channel_write_buffer(Channel *channel, ChannelBuffer *buffer,
|
|
PUCHAR chunk, ULONG chunkLength, PULONG bytesWritten)
|
|
{
|
|
// Is there enough storage space?
|
|
if (buffer->currentSize + chunkLength > buffer->totalSize)
|
|
{
|
|
PUCHAR newBuffer = NULL;
|
|
ULONG newSize = 0;
|
|
|
|
// Calculate the new buffer size
|
|
newSize = buffer->currentSize + chunkLength;
|
|
newSize += CHANNEL_CHUNK_SIZE + (newSize & (CHANNEL_CHUNK_SIZE - 1));
|
|
|
|
// Allocate the storage for the new data
|
|
if (buffer->totalSize)
|
|
{
|
|
newBuffer = (PUCHAR)realloc(buffer->buffer, newSize);
|
|
}
|
|
else
|
|
{
|
|
newBuffer = (PUCHAR)malloc(newSize);
|
|
}
|
|
|
|
// Allocation failure?
|
|
if (!newBuffer)
|
|
{
|
|
SAFE_FREE(buffer->buffer);
|
|
|
|
memset(buffer, 0, sizeof(ChannelBuffer));
|
|
|
|
return;
|
|
}
|
|
|
|
// Populate the buffer with the updated information
|
|
buffer->buffer = newBuffer;
|
|
buffer->totalSize = newSize;
|
|
}
|
|
|
|
// Copy the chunk data into the buffer
|
|
memcpy(buffer->buffer + buffer->currentSize, chunk, chunkLength);
|
|
|
|
// Update the current size
|
|
buffer->currentSize += chunkLength;
|
|
|
|
if (bytesWritten)
|
|
{
|
|
*bytesWritten = chunkLength;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reads a given number of bytes from the front of the buffer,
|
|
* thus removing the data from the buffer.
|
|
*/
|
|
VOID channel_read_buffer(Channel *channel, ChannelBuffer *buffer, PUCHAR chunk,
|
|
ULONG chunkLength, PULONG bytesRead)
|
|
{
|
|
ULONG actualSize = chunkLength;
|
|
|
|
// Ensure that data is not read past the end of the buffer
|
|
if (actualSize > buffer->currentSize)
|
|
{
|
|
actualSize = buffer->currentSize;
|
|
}
|
|
|
|
// Copy the front portion of the buffer into the chunk
|
|
memcpy(chunk, buffer->buffer, actualSize);
|
|
|
|
// Move the buffer forward if there is any left
|
|
if (actualSize != buffer->currentSize)
|
|
{
|
|
memcpy(buffer->buffer, buffer->buffer + actualSize,
|
|
buffer->currentSize - actualSize);
|
|
}
|
|
|
|
// Decrement the current used size of the buffer
|
|
buffer->currentSize -= actualSize;
|
|
|
|
// Pass back the number of bytes actually read
|
|
if (bytesRead)
|
|
{
|
|
*bytesRead = actualSize;
|
|
}
|
|
}
|