windows-nt/Source/XPSP1/NT/net/rras/ip/nathlp/ftp/ftpmsg.cpp

618 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ftpmsg.c
Abstract:
This module contains code for the FTP transparent proxy's
message-processing.
Author:
Qiang Wang (qiangw) 10-Apr-2000
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ipnatapi.h>
#define MAKE_ADDRESS(a,b,c,d) \
((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
#define MAKE_PORT(a,b) ((a) | ((b) << 8))
#define TOUPPER(c) ((c) > 'z' ? (c) : ((c) < 'a' ? (c) : (c) ^ 0x20))
//
// Constant string for the 'PASV' command reply
//
static CONST CHAR PasvReply[] = "227 ";
//
// Constant string for the 'PORT' command (must be upper-case)
//
static CONST CHAR PortCommand[] = "PORT ";
static CONST CHAR Eol[] = "\x0d\x0a\x00\x51\x69\x61\x6e\x67\x20\x57\x61\x6e\x67";
//
// FORWARD DECLARATIONS
//
BOOLEAN
FtppExtractOctet(
CHAR **Buffer,
CHAR *BufferEnd,
UCHAR *Octet
);
VOID
FtppWriteOctet(
CHAR **Buffer,
UCHAR Octet
);
VOID
FtpProcessMessage(
PFTP_INTERFACE Interfacep,
PFTP_ENDPOINT Endpointp,
PNH_BUFFER Bufferp
)
/*++
Routine Description:
This routine is called to process a full message read from an FTP
client or server on a control channel.
Arguments:
Interfacep - the interface on which the control-channel was accepted
Endpointp - the active endpoint corresponding to the control channel
Bufferp - contains the message read, along with other context information
Return Value:
none.
Notes:
Invoked with the interface's lock held by the caller, and with two
references made to the interface on our behalf. It is this routine's
responsibility to release both the references and the buffer.
--*/
{
BOOLEAN Success;
BOOLEAN Continuation;
SOCKET Socket;
ULONG Error;
ULONG i;
LONG NewLength;
ULONG PublicAddress;
USHORT PublicPort;
ULONG PrivateAddress;
USHORT PrivatePort;
UCHAR Numbers[6];
CHAR *HostPortStartp;
CHAR *CommandBufferp = reinterpret_cast<CHAR*>(Bufferp->Buffer);
CHAR *EndOfBufferp =
reinterpret_cast<CHAR*>(Bufferp->Buffer + Bufferp->TransferOffset);
CONST CHAR *Commandp =
Endpointp->Type == FtpClientEndpointType ?
(PCHAR)PasvReply : (PCHAR)PortCommand;
PROFILE("FtpProcessMessage");
if ((Bufferp->UserFlags & FTP_BUFFER_FLAG_FROM_ACTUAL_CLIENT) != 0) {
Socket = Endpointp->ClientSocket;
} else {
ASSERT((Bufferp->UserFlags & FTP_BUFFER_FLAG_FROM_ACTUAL_HOST) != 0);
Socket = Endpointp->HostSocket;
}
#if DBG
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: received (0x%08x) (%d) \"%s\"",
Bufferp->UserFlags, Bufferp->TransferOffset, CommandBufferp
);
#endif
if ((Bufferp->UserFlags & FTP_BUFFER_FLAG_FROM_ACTUAL_CLIENT) != 0 &&
(Bufferp->UserFlags & FTP_BUFFER_FLAG_CONTINUATION) == 0) {
while (*Commandp != '\0' && *Commandp == TOUPPER(*CommandBufferp)) {
Commandp++;
CommandBufferp++;
}
if (*Commandp == '\0') {
//
// We found a match.
//
if (Endpointp->Type == FtpClientEndpointType) {
//
// Skip non-numerical characters.
//
while (CommandBufferp < EndOfBufferp &&
(*CommandBufferp < '0' || *CommandBufferp > '9')) {
CommandBufferp++;
}
} else {
//
// Skip white space.
//
while (*CommandBufferp == ' ') {
CommandBufferp++;
}
}
HostPortStartp = CommandBufferp;
//
// Extract host and port numbers.
//
Success =
FtppExtractOctet(
&CommandBufferp,
EndOfBufferp,
&Numbers[0]
);
i = 1;
while (i < 6 && Success && *CommandBufferp == ',') {
CommandBufferp++;
Success =
FtppExtractOctet(
&CommandBufferp,
EndOfBufferp,
&Numbers[i]
);
i++;
}
if (i == 6 && Success) {
//
// We extract all of them successfully.
//
PrivateAddress =
MAKE_ADDRESS(
Numbers[0],
Numbers[1],
Numbers[2],
Numbers[3]
);
PrivatePort = MAKE_PORT(Numbers[4], Numbers[5]);
PublicAddress = Endpointp->BoundaryAddress;
if (PublicAddress == IP_NAT_ADDRESS_UNSPECIFIED) {
PublicAddress =
NhQueryAddressSocket(Endpointp->ClientSocket);
}
//
// Cancel the previous one first.
//
if (Endpointp->ReservedPort != 0) {
PTIMER_CONTEXT TimerContextp;
NatCancelRedirect(
FtpTranslatorHandle,
NAT_PROTOCOL_TCP,
Endpointp->DestinationAddress,
Endpointp->DestinationPort,
Endpointp->SourceAddress,
Endpointp->SourcePort,
Endpointp->NewDestinationAddress,
Endpointp->NewDestinationPort,
Endpointp->NewSourceAddress,
Endpointp->NewSourcePort
);
TimerContextp = reinterpret_cast<PTIMER_CONTEXT>(
NH_ALLOCATE(sizeof(TIMER_CONTEXT))
);
if (TimerContextp != NULL) {
TimerContextp->TimerQueueHandle = FtpTimerQueueHandle;
TimerContextp->ReservedPort = Endpointp->ReservedPort;
CreateTimerQueueTimer(
&(TimerContextp->TimerHandle),
FtpTimerQueueHandle,
FtpDelayedPortRelease,
(PVOID)TimerContextp,
FTP_PORT_RELEASE_DELAY,
0,
WT_EXECUTEDEFAULT
);
} else {
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage:"
" memory allocation failed for timer context"
);
NhErrorLog(
IP_FTP_LOG_ALLOCATION_FAILED,
0,
"%d",
sizeof(TIMER_CONTEXT)
);
}
Endpointp->ReservedPort = 0;
}
//
// Reserve a port for the new data session.
//
Error =
NatAcquirePortReservation(
FtpPortReservationHandle,
1,
&PublicPort
);
if (Error) {
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: error %d acquiring port",
Error
);
FtpDeleteActiveEndpoint(Endpointp);
FTP_DEREFERENCE_INTERFACE(Interfacep);
return;
}
Endpointp->ReservedPort = PublicPort;
//
// Create a redirect for the new data session.
//
if (Endpointp->Type == FtpClientEndpointType) {
Endpointp->DestinationAddress = PublicAddress;
Endpointp->SourceAddress = 0;
Endpointp->NewDestinationAddress = PrivateAddress;
Endpointp->NewSourceAddress = 0;
Endpointp->DestinationPort = PublicPort;
Endpointp->SourcePort = 0;
Endpointp->NewDestinationPort = PrivatePort;
Endpointp->NewSourcePort = 0;
Error =
NatCreatePartialRedirect(
FtpTranslatorHandle,
NatRedirectFlagLoopback,
NAT_PROTOCOL_TCP,
PublicAddress,
PublicPort,
PrivateAddress,
PrivatePort,
NULL,
NULL,
NULL
);
} else {
Endpointp->DestinationAddress = PublicAddress;
Endpointp->SourceAddress = Endpointp->ActualHostAddress;
Endpointp->NewDestinationAddress = PrivateAddress;
Endpointp->NewSourceAddress = Endpointp->ActualHostAddress;
Endpointp->DestinationPort = PublicPort;
Endpointp->SourcePort = FTP_PORT_DATA;
Endpointp->NewDestinationPort = PrivatePort;
Endpointp->NewSourcePort = FTP_PORT_DATA;
Error =
NatCreateRedirect(
FtpTranslatorHandle,
NatRedirectFlagLoopback,
NAT_PROTOCOL_TCP,
PublicAddress,
PublicPort,
Endpointp->ActualHostAddress,
FTP_PORT_DATA,
PrivateAddress,
PrivatePort,
Endpointp->ActualHostAddress,
FTP_PORT_DATA,
NULL,
NULL,
NULL
);
}
if (Error) {
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: error %d creating redirect",
Error
);
FtpDeleteActiveEndpoint(Endpointp);
FTP_DEREFERENCE_INTERFACE(Interfacep);
return;
}
//
// Modify the FTP command.
//
Numbers[0] = (UCHAR)(PublicAddress & 0xff);
Numbers[1] = (UCHAR)((PublicAddress >> 8) & 0xff);
Numbers[2] = (UCHAR)((PublicAddress >> 16) & 0xff);
Numbers[3] = (UCHAR)((PublicAddress >> 24) & 0xff);
Numbers[4] = (UCHAR)(PublicPort & 0xff);
Numbers[5] = (UCHAR)((PublicPort >> 8) & 0xff);
NewLength = 17;
for (i = 0; i < 6; i++) {
if (Numbers[i] > 99) {
NewLength++;
} else if (Numbers[i] <= 9) {
NewLength--;
}
}
Bufferp->TransferOffset +=
NewLength - (ULONG)(CommandBufferp - HostPortStartp);
ASSERT(Bufferp->TransferOffset <= NH_BUFFER_SIZE);
MoveMemory(
HostPortStartp + NewLength,
CommandBufferp,
EndOfBufferp - CommandBufferp
);
FtppWriteOctet(&HostPortStartp, Numbers[0]);
i = 1;
do {
*HostPortStartp = ',';
HostPortStartp++;
FtppWriteOctet(&HostPortStartp, Numbers[i]);
i++;
} while (i < 6);
}
}
}
//
// Forward the message.
//
Continuation =
FtpIsFullMessage(
reinterpret_cast<CHAR*>(
&(Bufferp->Buffer[Bufferp->TransferOffset - 2])
),
2
) == NULL;
if (Continuation) {
Bufferp->UserFlags |= FTP_BUFFER_FLAG_CONTINUATION;
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: message to be continued (%d)",
Bufferp->TransferOffset
);
} else {
Bufferp->UserFlags &= ~(ULONG)FTP_BUFFER_FLAG_CONTINUATION;
}
#if DBG
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: written (%d) \"%s\"",
Bufferp->TransferOffset,
Bufferp->Buffer
);
#endif
Error =
FtpWriteActiveEndpoint(
Interfacep,
Endpointp,
Socket,
Bufferp,
Bufferp->TransferOffset,
Bufferp->UserFlags
);
if (Error) {
NhTrace(
TRACE_FLAG_FTP,
"FtpProcessMessage: deleting endpoint %d, "
"FtpWriteActiveEndpoint=%d",
Endpointp->EndpointId, Error
);
FtpDeleteActiveEndpoint(Endpointp);
}
}
CHAR *
FtpIsFullMessage(
CHAR *Bufferp,
ULONG Length
)
/*++
Routine Description:
This routine is called to determine whether the passed-in buffer includes
a full FTP command.
Arguments:
Bufferp - contains the message read, along with other context information
Return Value:
CHAR * - points to the start of the next FTP command, or NULL if no
complete FTP command is detected.
--*/
{
ULONG Count = Length;
CONST CHAR *CommandBufferp = Bufferp;
CONST CHAR *CommandDelimiter = Eol;
PROFILE("FtpIsFullMessage");
ASSERT(
Eol[0] != '\0' &&
(Eol[1] == '\0' || Eol[2] == '\0') &&
Eol[0] != Eol[1]
);
while (Count > 0 && *CommandDelimiter != '\0') {
if (*CommandBufferp == *CommandDelimiter) {
CommandDelimiter++;
CommandBufferp++;
Count--;
} else {
if (CommandDelimiter == Eol) {
CommandBufferp++;
Count--;
} else {
CommandDelimiter = Eol;
}
}
}
return *CommandDelimiter == '\0' ? (CHAR *)CommandBufferp : NULL;
}
VOID CALLBACK
FtpDelayedPortRelease(
PVOID Parameter,
BOOLEAN TimerOrWaitFired
)
/*++
Routine Description:
This routine is called to release a reserved port.
Arguments:
Parameter - callback context
TimerOrWaitFired - wait timed out
Return Value:
None.
--*/
{
PTIMER_CONTEXT TimerContextp = (PTIMER_CONTEXT)Parameter;
PROFILE("FtpDelayedPortRelease");
NatReleasePortReservation(
FtpPortReservationHandle,
TimerContextp->ReservedPort,
1
);
DeleteTimerQueueTimer(
TimerContextp->TimerQueueHandle,
TimerContextp->TimerHandle,
NULL
);
NH_FREE(TimerContextp);
}
BOOLEAN
FtppExtractOctet(
CHAR **Buffer,
CHAR *BufferEnd,
UCHAR *Octet
)
/*++
Routine Description:
This routine is called to extrcat an octet from a string.
Arguments:
Buffer - points to a pointer to a string where conversion starts; on
return it points to the pointer to the string where conversion ends
BufferEnd - points to the end of the string
Octet - points to a caller-suplied storage to store converted octet
Return Value:
BOOLEAN - TRUE if successfuly converted, FALSE otherwise.
--*/
{
BOOLEAN Success;
ULONG i = 0;
ULONG Value = 0;
while (i < 3 && **Buffer >= '0' && **Buffer <= '9') {
Value *= 10;
Value += **Buffer - '0';
(*Buffer)++;
i++;
}
Success = i > 0 && Value < 0x100;
if (Success) {
*Octet = (UCHAR)Value;
}
return Success;
}
VOID
FtppWriteOctet(
CHAR **Buffer,
UCHAR Octet
)
/*++
Routine Description:
This routine is called to convert an octet to a string.
Arguments:
Buffer - points to a pointer to a string where conversion starts; on
return it points to the pointer to the string where conversion ends
Octet - octet to convert
Return Value:
None.
--*/
{
UCHAR Value = Octet;
if (Octet > 99) {
**Buffer = '0' + Value / 100;
Value %= 100;
(*Buffer)++;
}
if (Octet > 9) {
**Buffer = '0' + Value / 10;
Value %= 10;
(*Buffer)++;
}
ASSERT(Value <= 9);
**Buffer = '0' + Value;
(*Buffer)++;
}