1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-05-12 19:04:32 +02:00
Stephen Fewer 5a0d64211e Commit the Meterpreter C side for the UDP socket pivoting. (+1 bug fix for the TCP client socket notify event function)
git-svn-id: file:///home/svn/framework3/trunk@8430 4d416f70-5f16-0410-b530-b9f4589650da
2010-02-09 16:43:33 +00:00

436 lines
12 KiB
C

#include "precomp.h"
#include "tcp.h"
/*********************************
* TCP Client Channel Operations *
*********************************/
/*
* Writes data from the remote half of the channel to the established connection.
*/
DWORD tcp_channel_client_write( Channel *channel, Packet *request, LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten)
{
DWORD dwResult = ERROR_SUCCESS;
TcpClientContext * ctx = NULL;
LONG written = 0;
do
{
dprintf( "[TCP] tcp_channel_client_write. channel=0x%08X, buffsize=%d", channel, bufferSize );
ctx = (TcpClientContext *)context;
if( !ctx )
BREAK_WITH_ERROR( "[TCP] tcp_channel_client_write. ctx == NULL", ERROR_INVALID_HANDLE );
written = send( ctx->fd, buffer, bufferSize, 0 );
if( written == SOCKET_ERROR )
{
dwResult = WSAGetLastError();
if( dwResult == WSAEWOULDBLOCK )
{
struct timeval tv = {0};
fd_set set = {0};
DWORD res = 0;
dprintf( "[TCP] tcp_channel_client_write. send returned WSAEWOULDBLOCK, waiting until we can send again..." );
while( TRUE )
{
tv.tv_sec = 0;
tv.tv_usec = 1000;
FD_ZERO( &set );
FD_SET( ctx->fd, &set );
res = select( 0, NULL, &set, NULL, &tv );
if( res > 0 )
{
dwResult = ERROR_SUCCESS;
break;
}
else if( res == SOCKET_ERROR )
{
dwResult = WSAGetLastError();
break;
}
Sleep( 100 );
}
if( dwResult == ERROR_SUCCESS )
continue;
else
dprintf( "[TCP] tcp_channel_client_write. select == SOCKET_ERROR. dwResult=%d", dwResult );
}
written = 0;
dprintf( "[TCP] tcp_channel_client_write. written == SOCKET_ERROR. dwResult=%d", dwResult );
}
if( bytesWritten )
*bytesWritten = written;
} while( 0 );
dprintf( "[TCP] tcp_channel_client_write. finished. dwResult=%d, written=%d", dwResult, written );
return dwResult;
}
/*
* Closes the established connection and cleans up stale state
*/
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
*/
DWORD tcp_channel_client_local_notify( Remote * remote, TcpClientContext * ctx )
{
struct timeval tv = {0};
fd_set set = {0};
UCHAR buf[16384] = {0};
LONG dwBytesRead = 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
{
// Reset the notification event
ResetEvent( ctx->notify );
FD_ZERO( &set );
FD_SET( ctx->fd, &set );
tv.tv_sec = 0;
tv.tv_usec = 0;
// Read data from the client connection
dwBytesRead = recv( ctx->fd, buf, sizeof(buf), 0 );
if( dwBytesRead == SOCKET_ERROR )
{
DWORD dwError = WSAGetLastError();
// WSAECONNRESET: The connection was forcibly closed by the remote host.
// WSAECONNABORTED: The connection was terminated due to a time-out or other failure.
if( dwError == WSAECONNRESET || dwError == WSAECONNABORTED )
{
dprintf( "[TCP] tcp_channel_client_local_notify. [error] closing down channel gracefully. WSAGetLastError=%d", dwError );
// By setting bytesRead to zero, we can ensure we close down the channel gracefully...
dwBytesRead = 0;
}
else if( dwError == WSAEWOULDBLOCK )
{
dprintf( "[TCP] tcp_channel_client_local_notify. channel=0x%08X. recv generated a WSAEWOULDBLOCK", ctx->channel );
// break and let the scheduler notify us again if needed.
break;
}
else
{
dprintf( "[TCP] tcp_channel_client_local_notify. [error] channel=0x%08X read=0x%.8x (ignored). WSAGetLastError=%d", ctx->channel, dwBytesRead, dwError );
// we loop again because bytesRead is -1.
}
}
if( dwBytesRead == 0 )
{
dprintf( "[TCP] tcp_channel_client_local_notify. [closed] channel=0x%08X read=0x%.8x", ctx->channel, dwBytesRead );
// 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;
}
else if( dwBytesRead > 0 )
{
if( ctx->channel )
{
dprintf( "[TCP] tcp_channel_client_local_notify. [data] channel=0x%08X read=%d", ctx->channel, dwBytesRead );
channel_write( ctx->channel, ctx->remote, NULL, 0, buf, dwBytesRead, 0 );
}
else
{
dprintf( "[TCP] tcp_channel_client_local_notify. [data] channel=<invalid> read=0x%.8x", dwBytesRead );
}
}
} while( select( 1, &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;
// 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)
{
DWORD dwResult = ERROR_SUCCESS;
Packet * response = NULL;
SocketContext * ctx = NULL;
Channel * channel = NULL;
DWORD cid = 0;
DWORD how = 0;
do
{
dprintf( "[TCP] entering request_net_socket_tcp_shutdown" );
response = packet_create_response( packet );
if( !response )
BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. response == NULL", ERROR_NOT_ENOUGH_MEMORY );
cid = packet_get_tlv_value_uint( packet, TLV_TYPE_CHANNEL_ID );
how = packet_get_tlv_value_uint( packet, TLV_TYPE_SHUTDOWN_HOW );
channel = channel_find_by_id( cid );
if( !response )
BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. channel == NULL", ERROR_INVALID_HANDLE );
dprintf( "[TCP] request_net_socket_tcp_shutdown. channel=0x%08X, cid=%d", channel, cid );
ctx = channel_get_native_io_context( channel );
if( !ctx )
BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. ctx == NULL", ERROR_INVALID_HANDLE );
if( shutdown( ctx->fd, how ) == SOCKET_ERROR )
BREAK_ON_WSAERROR( "[TCP] request_net_socket_tcp_shutdown. shutdown failed" );
// sf: we dont seem to need to call this here, as the channels tcp_channel_client_local_notify() will
// catch the socket closure and call free_socket_context() for us, due the the FD_READ|FD_CLOSE flags
// being passed to WSAEventSelect for the notify event in create_tcp_client_channel().
// This avoids a double call (from two different threads) and subsequent access violation in some edge cases.
//free_socket_context( ctx );
} while( 0 );
packet_transmit_response( dwResult, remote, response );
dprintf( "[TCP] leaving request_net_socket_tcp_shutdown" );
return ERROR_SUCCESS;
}