#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include "alias_local.h"
#define FTP_CONTROL_PORT_NUMBER 21
#define MAX_MESSAGE_SIZE 128
enum ftp_message_type {
FTP_PORT_COMMAND,
FTP_EPRT_COMMAND,
FTP_227_REPLY,
FTP_229_REPLY,
FTP_UNKNOWN_MESSAGE
};
static int ParseFtpPortCommand(char *, int);
static int ParseFtpEprtCommand(char *, int);
static int ParseFtp227Reply(char *, int);
static int ParseFtp229Reply(char *, int);
static void NewFtpMessage(struct ip *, struct alias_link *, int, int);
static struct in_addr true_addr;
static u_short true_port;
void
AliasHandleFtpOut(
struct ip *pip,
struct alias_link *link,
int maxpacketsize )
{
int hlen, tlen, dlen;
char *sptr;
struct tcphdr *tc;
int ftp_message_type;
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
sptr = (char *) pip;
sptr += hlen;
if (dlen <= MAX_MESSAGE_SIZE && GetLastLineCrlfTermed(link)) {
ftp_message_type = FTP_UNKNOWN_MESSAGE;
if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
if (ParseFtpPortCommand(sptr, dlen))
ftp_message_type = FTP_PORT_COMMAND;
else if (ParseFtpEprtCommand(sptr, dlen))
ftp_message_type = FTP_EPRT_COMMAND;
} else {
if (ParseFtp227Reply(sptr, dlen))
ftp_message_type = FTP_227_REPLY;
else if (ParseFtp229Reply(sptr, dlen))
ftp_message_type = FTP_229_REPLY;
}
if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
NewFtpMessage(pip, link, maxpacketsize, ftp_message_type);
}
if (dlen) {
sptr = (char *) pip;
tlen = ntohs(pip->ip_len);
SetLastLineCrlfTermed(link,
(sptr[tlen-2] == '\r') && (sptr[tlen-1] == '\n'));
}
}
static int
ParseFtpPortCommand(char *sptr, int dlen)
{
char ch;
int i, state;
u_int32_t addr;
u_short port;
u_int8_t octet;
if (dlen < 18)
return 0;
addr = port = octet = 0;
state = -4;
for (i = 0; i < dlen; i++) {
ch = sptr[i];
switch (state) {
case -4: if (ch == 'P') state++; else return 0; break;
case -3: if (ch == 'O') state++; else return 0; break;
case -2: if (ch == 'R') state++; else return 0; break;
case -1: if (ch == 'T') state++; else return 0; break;
case 0:
if (isspace(ch))
break;
else
state++;
case 1: case 3: case 5: case 7: case 9: case 11:
if (isdigit(ch)) {
octet = ch - '0';
state++;
} else
return 0;
break;
case 2: case 4: case 6: case 8:
if (isdigit(ch))
octet = 10 * octet + ch - '0';
else if (ch == ',') {
addr = (addr << 8) + octet;
state++;
} else
return 0;
break;
case 10: case 12:
if (isdigit(ch))
octet = 10 * octet + ch - '0';
else if (ch == ',' || state == 12) {
port = (port << 8) + octet;
state++;
} else
return 0;
break;
}
}
if (state == 13) {
true_addr.s_addr = htonl(addr);
true_port = port;
return 1;
} else
return 0;
}
static int
ParseFtpEprtCommand(char *sptr, int dlen)
{
char ch, delim;
int i, state;
u_int32_t addr;
u_short port;
u_int8_t octet;
if (dlen < 18)
return 0;
addr = port = octet = 0;
delim = '|';
state = -4;
for (i = 0; i < dlen; i++) {
ch = sptr[i];
switch (state)
{
case -4: if (ch == 'E') state++; else return 0; break;
case -3: if (ch == 'P') state++; else return 0; break;
case -2: if (ch == 'R') state++; else return 0; break;
case -1: if (ch == 'T') state++; else return 0; break;
case 0:
if (!isspace(ch)) {
delim = ch;
state++;
}
break;
case 1:
if (ch == '1')
state++;
else
return 0;
break;
case 2:
if (ch == delim)
state++;
else
return 0;
break;
case 3: case 5: case 7: case 9:
if (isdigit(ch)) {
octet = ch - '0';
state++;
} else
return 0;
break;
case 4: case 6: case 8: case 10:
if (isdigit(ch))
octet = 10 * octet + ch - '0';
else if (ch == '.' || state == 10) {
addr = (addr << 8) + octet;
state++;
} else
return 0;
break;
case 11:
if (isdigit(ch)) {
port = ch - '0';
state++;
} else
return 0;
break;
case 12:
if (isdigit(ch))
port = 10 * port + ch - '0';
else if (ch == delim)
state++;
else
return 0;
break;
}
}
if (state == 13) {
true_addr.s_addr = htonl(addr);
true_port = port;
return 1;
} else
return 0;
}
static int
ParseFtp227Reply(char *sptr, int dlen)
{
char ch;
int i, state;
u_int32_t addr;
u_short port;
u_int8_t octet;
if (dlen < 17)
return 0;
addr = port = octet = 0;
state = -3;
for (i = 0; i < dlen; i++) {
ch = sptr[i];
switch (state)
{
case -3: if (ch == '2') state++; else return 0; break;
case -2: if (ch == '2') state++; else return 0; break;
case -1: if (ch == '7') state++; else return 0; break;
case 0:
if (ch == '(')
state++;
break;
case 1: case 3: case 5: case 7: case 9: case 11:
if (isdigit(ch)) {
octet = ch - '0';
state++;
} else
return 0;
break;
case 2: case 4: case 6: case 8:
if (isdigit(ch))
octet = 10 * octet + ch - '0';
else if (ch == ',') {
addr = (addr << 8) + octet;
state++;
} else
return 0;
break;
case 10: case 12:
if (isdigit(ch))
octet = 10 * octet + ch - '0';
else if (ch == ',' || (state == 12 && ch == ')')) {
port = (port << 8) + octet;
state++;
} else
return 0;
break;
}
}
if (state == 13) {
true_port = port;
true_addr.s_addr = htonl(addr);
return 1;
} else
return 0;
}
static int
ParseFtp229Reply(char *sptr, int dlen)
{
char ch, delim;
int i, state;
u_short port;
if (dlen < 11)
return 0;
port = 0;
delim = '|';
state = -3;
for (i = 0; i < dlen; i++) {
ch = sptr[i];
switch (state)
{
case -3: if (ch == '2') state++; else return 0; break;
case -2: if (ch == '2') state++; else return 0; break;
case -1: if (ch == '9') state++; else return 0; break;
case 0:
if (ch == '(')
state++;
break;
case 1:
delim = ch;
state++;
break;
case 2: case 3:
if (ch == delim)
state++;
else
return 0;
break;
case 4:
if (isdigit(ch)) {
port = ch - '0';
state++;
} else
return 0;
break;
case 5:
if (isdigit(ch))
port = 10 * port + ch - '0';
else if (ch == delim)
state++;
else
return 0;
break;
case 6:
if (ch == ')')
state++;
else
return 0;
break;
}
}
if (state == 7) {
true_port = port;
return 1;
} else
return 0;
}
static void
NewFtpMessage(struct ip *pip,
struct alias_link *link,
int maxpacketsize,
int ftp_message_type)
{
struct alias_link *ftp_link;
if (ftp_message_type != FTP_229_REPLY &&
pip->ip_src.s_addr != true_addr.s_addr)
return;
if (true_port < IPPORT_RESERVED)
return;
ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
htons(true_port), 0, IPPROTO_TCP, 1);
if (ftp_link != NULL)
{
int slen, hlen, tlen, dlen;
struct tcphdr *tc;
#ifndef NO_FW_PUNCH
if (ftp_message_type == FTP_PORT_COMMAND ||
ftp_message_type == FTP_EPRT_COMMAND) {
PunchFWHole(ftp_link);
}
#endif
tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
hlen = (pip->ip_hl + tc->th_off) << 2;
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
{
char stemp[MAX_MESSAGE_SIZE + 1];
char *sptr;
u_short alias_port;
u_char *ptr;
int a1, a2, a3, a4, p1, p2;
struct in_addr alias_address;
alias_address = GetAliasAddress(link);
ptr = (u_char *) &alias_address.s_addr;
a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
alias_port = GetAliasPort(ftp_link);
switch (ftp_message_type)
{
case FTP_PORT_COMMAND:
case FTP_227_REPLY:
ptr = (u_char *) &alias_port;
p1 = *ptr++; p2=*ptr;
if (ftp_message_type == FTP_PORT_COMMAND) {
snprintf(stemp, sizeof(stemp), "PORT %d,%d,%d,%d,%d,%d\r\n",
a1,a2,a3,a4,p1,p2);
} else {
snprintf(stemp, sizeof(stemp),
"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
a1,a2,a3,a4,p1,p2);
}
break;
case FTP_EPRT_COMMAND:
snprintf(stemp, sizeof(stemp), "EPRT |1|%d.%d.%d.%d|%d|\r\n",
a1,a2,a3,a4,ntohs(alias_port));
break;
case FTP_229_REPLY:
snprintf(stemp, sizeof(stemp), "229 Entering Extended Passive Mode (|||%d|)\r\n",
ntohs(alias_port));
break;
}
slen = strlen(stemp);
sptr = (char *) pip; sptr += hlen;
strncpy(sptr, stemp, maxpacketsize-hlen);
}
{
int delta;
SetAckModified(link);
delta = GetDeltaSeqOut(pip, link);
AddSeq(pip, link, delta+slen-dlen);
}
{
u_short new_len;
new_len = htons(hlen + slen);
DifferentialChecksum(&pip->ip_sum,
&new_len,
&pip->ip_len,
1);
pip->ip_len = new_len;
}
tc->th_sum = 0;
tc->th_sum = TcpChecksum(pip);
}
else
{
#ifdef DEBUG
fprintf(stderr,
"PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
#endif
}
}