1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-18 07:11:12 +02:00
HD Moore 489c5adb98 Fixes by adding a 250ms sleep to the dispatch of the close call.
git-svn-id: file:///home/svn/framework3/trunk@7934 4d416f70-5f16-0410-b530-b9f4589650da
2009-12-21 19:53:10 +00:00

359 lines
9.6 KiB
C

#include "precomp.h"
/*********************************
* TCP Client Channel Operations *
*********************************/
/*
* Writes data from the remote half of the channel to the established
* connection.
*/
static DWORD tcp_channel_client_write(Channel *channel, Packet *request,
LPVOID context, LPVOID buffer, DWORD bufferSize,
LPDWORD bytesWritten)
{
TcpClientContext *ctx = (TcpClientContext *)context;
DWORD result= ERROR_SUCCESS;
LONG written = 0;
dprintf( "[TCP] tcp_channel_client_write. channel=0x%08X, buffsize=%d", channel, bufferSize );
// Write a chunk
if ((written = send(ctx->fd, buffer, bufferSize, 0)) <= 0)
{
written = 0;
result = GetLastError();
}
// Set bytesWritten
if (bytesWritten)
*bytesWritten = written;
return result;
}
/*
* Closes the established connection and cleans up stale state
*/
static DWORD tcp_channel_client_close(Channel *channel, Packet *request,
LPVOID context)
{
TcpClientContext *ctx = (TcpClientContext *)context;
dprintf( "[TCP] tcp_channel_client_close. channel=0x%08X, ctx=0x%08X", channel, ctx );
if (ctx)
{
// Set the context channel to NULL so we don't try to close the
// channel (since it's already being closed)
ctx->channel = NULL;
// Free the context
free_tcp_client_context(ctx);
// Set the native channel operations context to NULL
channel_set_native_io_context(channel, NULL);
}
return ERROR_SUCCESS;
}
/*
* Callback for when there is data available on the local side of the TCP
* client connection
*/
static DWORD tcp_channel_client_local_notify(Remote *remote,
TcpClientContext *ctx)
{
struct timeval tv;
fd_set set;
UCHAR buf[16384];
LONG bytesRead;
// Reset the notification event
ResetEvent(ctx->notify);
tv.tv_sec = 0;
tv.tv_usec = 0;
// We select in a loop with a zero second timeout because it's possible
// that we could get a recv notification and a close notification at once,
// so we need some way to make sure that we see them both, otherwise the
// event handle wont get re set to notify us.
do
{
FD_ZERO(&set);
FD_SET(ctx->fd, &set);
// Read data from the client connection
bytesRead = recv(ctx->fd, buf, sizeof(buf), 0);
// Not sure why we get these with pending data
if(bytesRead == SOCKET_ERROR) {
printf( "[TCP] tcp_channel_client_local_notify. [error] channel=0x%08X read=0x%.8x (ignored)", ctx->channel, bytesRead );
continue;
}
if (bytesRead == 0) {
dprintf( "[TCP] tcp_channel_client_local_notify. [closed] channel=0x%08X read=0x%.8x", ctx->channel, bytesRead );
// Set the native channel operations context to NULL
channel_set_native_io_context(ctx->channel, NULL);
// Sleep for a quarter second
Sleep(250);
// Free the context
free_tcp_client_context(ctx);
// Stop processing
break;
}
if (ctx->channel) {
// dprintf( "[TCP] tcp_channel_client_local_notify. [data] channel=0x%08X read=0x%.8x", ctx->channel, bytesRead );
channel_write(ctx->channel, ctx->remote, NULL, 0, buf, bytesRead, 0);
} else {
dprintf( "[TCP] tcp_channel_client_local_notify. [data] channel=<invalid> read=0x%.8x", bytesRead );
}
} while (select(0, &set, NULL, NULL, &tv) > 0);
return ERROR_SUCCESS;
}
/*
* Allocates a streaming TCP channel
*
* TLVs:
*
* req: TLV_TYPE_HOST_NAME - The host to connect to
* req: TLV_TYPE_PORT - The port to connect to
*/
DWORD request_net_tcp_client_channel_open(Remote *remote, Packet *packet)
{
Channel *channel = NULL;
Packet *response = packet_create_response(packet);
DWORD result = ERROR_SUCCESS;
LPCSTR host;
DWORD port;
do
{
// No response packet?
if (!response)
break;
// Extract the hostname and port that we are to connect to
host = packet_get_tlv_value_string(packet, TLV_TYPE_PEER_HOST);
port = packet_get_tlv_value_uint(packet, TLV_TYPE_PEER_PORT);
// Open the TCP channel
if ((result = create_tcp_client_channel(remote, host,
(USHORT)(port & 0xffff), &channel)) != ERROR_SUCCESS)
break;
// Set the channel's identifier on the response
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,
channel_get_id(channel));
} while (0);
// Transmit the response
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
/*
* Creates a connection to a remote host and builds a logical channel to
* represent it.
*
*/
DWORD create_tcp_client_channel(Remote *remote, LPCSTR remoteHost,
USHORT remotePort, Channel **outChannel)
{
StreamChannelOps chops;
TcpClientContext *ctx = NULL;
DWORD result = ERROR_SUCCESS;
Channel *channel = NULL;
struct sockaddr_in s;
SOCKET clientFd = 0;
if (outChannel)
*outChannel = NULL;
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d", remoteHost, remotePort );
do
{
// Allocate a client socket
if ((clientFd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0))
== INVALID_SOCKET)
{
clientFd = 0;
result = GetLastError();
break;
}
s.sin_family = AF_INET;
s.sin_port = htons(remotePort);
s.sin_addr.s_addr = inet_addr(remoteHost);
// Resolve the host name locally
if (s.sin_addr.s_addr == (DWORD)-1)
{
struct hostent *h;
if (!(h = gethostbyname(remoteHost)))
{
result = GetLastError();
break;
}
memcpy(&s.sin_addr.s_addr, h->h_addr, h->h_length);
}
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d connecting...", remoteHost, remotePort );
// Try to connect to the host/port
if (connect(clientFd, (struct sockaddr *)&s, sizeof(s)) == SOCKET_ERROR)
{
result = GetLastError();
break;
}
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d connected!", remoteHost, remotePort );
// Allocate the client context for tracking the connection
if (!(ctx = (TcpClientContext *)malloc(
sizeof(TcpClientContext))))
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Initialize the context attributes
memset(ctx, 0, sizeof(TcpClientContext));
ctx->remote = remote;
ctx->fd = clientFd;
ctx->mutex = CreateMutex(NULL, FALSE, NULL);
// Initialize the channel operations structure
memset(&chops, 0, sizeof(chops));
chops.native.context = ctx;
chops.native.write = tcp_channel_client_write;
chops.native.close = tcp_channel_client_close;
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d creating the channel", remoteHost, remotePort );
// Allocate an uninitialized channel for associated with this connection
if (!(channel = channel_create_stream(0, 0,
&chops)))
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Save the channel context association
ctx->channel = channel;
// Finally, create a waitable event and insert it into the scheduler's
// waitable list
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d creating the notify", remoteHost, remotePort );
if ((ctx->notify = WSACreateEvent()))
{
WSAEventSelect(ctx->fd, ctx->notify, FD_READ|FD_CLOSE);
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d created the notify %.8x", remoteHost, remotePort, ctx->notify );
scheduler_insert_waitable( ctx->notify, ctx,
(WaitableNotifyRoutine)tcp_channel_client_local_notify);
}
} while (0);
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d all done", remoteHost, remotePort );
// Clean up on failure
if (result != ERROR_SUCCESS)
{
dprintf( "[TCP] create_tcp_client_channel. host=%s, port=%d cleaning up failed connection", remoteHost, remotePort );
if (ctx)
free_tcp_client_context(ctx);
if (clientFd)
closesocket(clientFd);
channel = NULL;
}
if (outChannel)
*outChannel = channel;
return result;
}
/*
* Deallocates and cleans up the attributes of a socket context
*/
VOID free_socket_context(SocketContext *ctx)
{
dprintf( "[TCP] free_socket_context. ctx=0x%08X", ctx );
// Close the socket and notification handle
if (ctx->fd){
closesocket(ctx->fd);
ctx->fd = 0;
}
if (ctx->channel) {
channel_close(ctx->channel, ctx->remote, NULL, 0, NULL);
ctx->channel = NULL;
}
if (ctx->notify)
{
dprintf( "[TCP] free_socket_context. remove_waitable ctx=0x%08X notify=0x%08X", ctx, ctx->notify);
// The scheduler calls CloseHandle on our WSACreateEvent() for us
scheduler_remove_waitable(ctx->notify);
ctx->notify = NULL;
}
// Free the context
free(ctx);
}
/*
* Shuts the socket down for either reading or writing based on the how
* parameter supplied by the remote side
*/
DWORD request_net_socket_tcp_shutdown(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
SocketContext *ctx = NULL;
Channel *channel = NULL;
DWORD result = ERROR_SUCCESS;
DWORD how;
dprintf( "[TCP] entering request_net_socket_tcp_shutdown" );
// Find the associated channel
channel = channel_find_by_id(packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID));
how = packet_get_tlv_value_uint(packet, TLV_TYPE_SHUTDOWN_HOW);
dprintf( "[TCP] request_net_socket_tcp_shutdown. channel=0x%08X", channel );
// If the channel and channel context are valid...
if ((channel) &&
((ctx = channel_get_native_io_context(channel))))
{
if (shutdown(ctx->fd, how) == SOCKET_ERROR)
result = WSAGetLastError();
else
free_socket_context( ctx );
}
packet_transmit_response(result, remote, response);
dprintf( "[TCP] leaving request_net_socket_tcp_shutdown" );
return ERROR_SUCCESS;
}