0
mirror of https://github.com/Indemsys/Frequency_Inverter.git synced 2026-05-09 06:12:58 +00:00
Files
2022-01-04 12:22:53 +02:00

589 lines
19 KiB
C

/**HEADER********************************************************************
*
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
***************************************************************************
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: natftp.c$
* $Version : 3.8.5.0$
* $Date : Sep-21-2012$
*
* Comments:
*
* This file contains the NAT FTP ALG.
*
*END************************************************************************/
#include <rtcs.h>
#include <rtcs_prv.h>
#include <fio.h>
#include <tftp.h>
#include <ctype.h>
#include <string.h>
#if RTCSCFG_ENABLE_NAT
#include "nat.h"
#include "nat_prv.h"
/*****************************************
**
** NAT FTP ALG Private structures
*/
typedef struct nat_ftp_struct{
NAT_ALG_CFG_STRUCT HEADER;
_rtcs_part FTP_PART_ID;
} NAT_FTP_STRUCT, _PTR_ NAT_FTP_STRUCT_PTR;
typedef struct nat_session_ftp_struct {
NAT_SESSION_EXT_STRUCT HEADER;
int_32 LAST_ADJ;
uint_32 LAST_SEQ;
int_32 NEXT_ADJ;
uint_32 NEXT_SEQ;
} NAT_SESSION_FTP_STRUCT, _PTR_ NAT_SESSION_FTP_STRUCT_PTR;
struct {
IP_HEADER IP;
TCP_HEADER TCP;
} NAT_ALG_FTP_dummy = {
{
{0x45}, /* VERSLEN */
{0x00}, /* TOS */
{0x00, 0x28}, /* LENGTH */
{0xA1, 0x10}, /* ID */
{0x40, 0x00}, /* FRAGMENT */
{0x80}, /* TTL */
{0x06}, /* PROTOCOL */
{0xF8, 0xE8}, /* CHECKSUM */
{0x00, 0x00, 0x00, 0x00}, /* SOURCE */
{0x00, 0x00, 0x00, 0x00} /* DESTINATION */
},
{
{0x00, 0x00}, /* SOURCE PORT */
{0x00, 0x00}, /* DEST PORT */
{0x00, 0x00, 0x00, 0x00}, /* SEQ */
{0x00, 0x00, 0x00, 0x00}, /* ACK */
{0x50, 0x10}, /* FLAGS */
{0x22, 0x38}, /* WINDOW */
{0x00, 0x00}, /* CHECKSUM */
{0x00, 0x00} /* URGENT */
}
};
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : NAT_ALG_ftp
* Returned Value : error code
* Comments :
* Initializes the NAT FTP ALG.
*
*END*-----------------------------------------------------------------*/
uint_32 NAT_ALG_ftp
(
pointer ptr /* [IN] Pointer to NAT config struct */
)
{ /* Body */
NAT_CFG_STRUCT_PTR nat_cfg_ptr = ptr;
NAT_FTP_STRUCT_PTR ftp_ptr;
ftp_ptr = _mem_alloc_system(sizeof(NAT_FTP_STRUCT));
if (ftp_ptr) {
ftp_ptr->FTP_PART_ID = RTCS_part_create(
sizeof(NAT_SESSION_FTP_STRUCT), 4, 4, 0, NULL, NULL);
if (ftp_ptr->FTP_PART_ID) {
ftp_ptr->HEADER.NEXT = nat_cfg_ptr->ALG_INFO_PTR;
nat_cfg_ptr->ALG_INFO_PTR = (NAT_ALG_CFG_STRUCT_PTR)(pointer)ftp_ptr;
ftp_ptr->HEADER.ALG_TYPE = NAT_ALG_FTP_TYPE;
ftp_ptr->HEADER.ALG_EXEC = NAT_ALG_ftp_apply;
return RTCS_OK;
} else {
_mem_free((pointer)ftp_ptr);
} /* Endif */
} /* Endif */
return RTCSERR_OUT_OF_MEMORY;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : NAT_ALG_detect_port
* Returned Value : boolean
* Comments :
* Detects a "PORT" command. Assumes data starts with "PORT".
*
*END*-----------------------------------------------------------------*/
static boolean NAT_ALG_detect_port
(
uchar_ptr data_ptr, /* [IN] pointer to data */
uint_32 len, /* [IN] length of data */
uint_32_ptr ip_address_ptr, /* [OUT] address read from data */
uint_16_ptr port_ptr /* [OUT] port read from data */
)
{ /* Body */
uchar buf[6] = { 0, 0, 0, 0, 0, 0 };
uchar_ptr ptr;
uint_32 state;
*ip_address_ptr = 0;
*port_ptr = 0;
/* "PORT a,a,a,a,p,p" 16 bytes minimum */
if (len < 18) {
return FALSE;
} /* Endif */
/* Skip "PORT" */
ptr = data_ptr + 4;
/*
** Regular expression for PORT command: ' '+([0-9]{1,3},){5}[0-9]{1,3}
*/
/* At least 1 space */
if (*ptr++ != ' ') {
return FALSE;
} /* Endif */
/* Skip all spaces */
while ((ptr < (data_ptr + len)) && (*ptr == ' ')) {
ptr++;
} /* Enwhile */
for (state = 0; ptr < (data_ptr + len); ptr++) {
if (isdigit(*ptr) && (state < 12)) {
if ((state & 1) == 0) {
state++;
} /* Endif */
buf[state/2] = buf[state/2] * 10 + *ptr - '0';
} else if ((*ptr == ',') && (state & 1)) {
state++;
} else {
break;
} /* Endif */
} /* Endfor */
if (state != 11) {
return FALSE;
} /* Endif */
*ip_address_ptr = ((buf[0] & 0xFF) << 24) +
((buf[1] & 0xFF) << 16) +
((buf[2] & 0xFF) << 8) +
(buf[3] & 0xFF);
*port_ptr = ((buf[4] & 0xFF) << 8) + (buf[5] & 0xFF);
return TRUE;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : NAT_ALG_ftp_pcb_free
* Returned Value : void
* Comments :
* Frees a PCB
*
*END*-----------------------------------------------------------------*/
static void NAT_ALG_ftp_pcb_free
(
PCB_PTR pcb_ptr /* [IN] PCB to free */
)
{ /* Body */
_mem_free(pcb_ptr);
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : NAT_ALG_ftp_bwrite
* Returned Value : void
* Comments :
* Writes the ascii representation of a byte
*
*END*-----------------------------------------------------------------*/
static void NAT_ALG_ftp_bwrite
(
uchar byte, /* [IN] Byte to write */
uchar_ptr _PTR_ store_ptr_ptr /* [IN/OUT] Pointer to data */
)
{
uchar_ptr store_ptr = *store_ptr_ptr;
uint_32 len = 1;
/* Determine length of byte */
if (byte > 99) {
len = 3;
} else if (byte > 9) {
len = 2;
} /* Endif */
/* Write out byte */
switch (len) {
case 3:
*store_ptr++ = '0' + byte / 100;
byte %= 100;
case 2:
*store_ptr++ = '0' + byte / 10;
byte %= 10;
case 1:
*store_ptr++ = '0' + byte;
break;
} /* Endif */
*store_ptr_ptr = store_ptr;
} /* Endbody */
/*FUNCTION*-------------------------------------------------------------
*
* Function Name : NAT_ALG_ftp_apply
* Returned Value : error code
* Comments :
* Applies the NAT FTP ALG to a packet.
*
*END*-----------------------------------------------------------------*/
uint_32 NAT_ALG_ftp_apply
(
RTCSPCB_PTR _PTR_ pcb_ptr_ptr,
boolean pub_to_prv,
pointer alg_cfg_ptr,
pointer _PTR_ session_ptr_ptr
)
{ /* Body */
NAT_CFG_STRUCT_PTR nat_cfg_ptr = RTCS_getcfg(NAT);
NAT_SESSION_STRUCT_PTR nat_session_ptr = *((NAT_SESSION_STRUCT_PTR *)session_ptr_ptr);
NAT_SESSION_STRUCT_PTR nat_data_session_ptr;
uchar_ptr orig_pcb_data_ptr = (pointer)RTCSPCB_DATA(*pcb_ptr_ptr), new_pcb_data_ptr;
IP_HEADER_PTR ip_header_ptr = (IP_HEADER_PTR)(pointer)orig_pcb_data_ptr;
TRANSPORT_UNION transport;
NAT_SESSION_FTP_STRUCT_PTR ftp_session_ptr;
RTCSPCB_PTR new_pcb_ptr;
uchar_ptr tcp_data_ptr;
boolean src_port_is_ftp, dest_port_is_ftp;
uint_32 ack, temp, seq, addr, error;
uint_16 orig_tcp_data_length, new_tcp_data_length;
int_32 seq_adj, i;
uint_16 chksum, port, ip_header_length, tcp_header_length;
uint_16 orig_ip_length, new_ip_length;
PCB_PTR new_mqx_pcb_ptr;
if (nat_session_ptr == NULL) {
/*
** There is no session yet. This packet may be part of the TCP handshake,
** and not a data message. Let NAT first create a session.
*/
return RTCS_OK;
} /* Endif */
/* FTP uses TCP only */
if (ntohc(ip_header_ptr->PROTOCOL) != IPPROTO_TCP) {
return RTCS_OK;
} /* Endif */
/* Extract port information */
transport.PTR = TRANSPORT_PTR(ip_header_ptr);
src_port_is_ftp = (ntohs(transport.TCP_PTR->source_port) == IPPORT_FTP);
dest_port_is_ftp = (ntohs(transport.TCP_PTR->dest_port) == IPPORT_FTP);
/* ALG is only applicable on FTP command sessions (typically port 21) */
if (!src_port_is_ftp && !dest_port_is_ftp) {
return RTCS_OK;
} /* Endif */
if (! ((src_port_is_ftp && pub_to_prv) || (dest_port_is_ftp && !pub_to_prv))) {
return RTCS_OK;
} /* Endif */
/* Get TCP data pointer */
ip_header_length = IPH_LEN(ip_header_ptr);
orig_ip_length = ntohs(ip_header_ptr->LENGTH);
tcp_header_length = (((ntohs(transport.TCP_PTR->flags)) & DATAOFS_MASK) >> (DATAOFS_SHIFT - 2));
tcp_data_ptr = &transport.DATA_OFFSET[tcp_header_length];
orig_tcp_data_length = orig_ip_length - (ip_header_length + tcp_header_length);
/* Check if an FTP session struct exists */
ftp_session_ptr = (NAT_SESSION_FTP_STRUCT_PTR)(pointer)nat_session_ptr->ALG_INFO_PTR;
while (ftp_session_ptr != NULL) {
if (ftp_session_ptr->HEADER.ALG_TYPE == NAT_ALG_FTP_TYPE) {
/* Found the FTP session struct */
break;
} /* Endif */
ftp_session_ptr = (NAT_SESSION_FTP_STRUCT_PTR)(pointer)ftp_session_ptr->HEADER.NEXT;
} /* Endwhile */
/*
** If the packet is entering the priv net from FTP command port, we don't
** modify it if no FTP session struct exists. The session may be using a mode
** that doesn't require any ALG.If there is a session struct, update ACK.
*/
if (src_port_is_ftp && pub_to_prv) {
if (ftp_session_ptr) {
/* Update packet with correct ACK */
ack = ntohl(transport.TCP_PTR->ack);
chksum = ntohs(transport.TCP_PTR->checksum);
if ((int_32)(ack - (ftp_session_ptr->LAST_SEQ + ftp_session_ptr->LAST_ADJ)) <= 0) {
temp = ftp_session_ptr->LAST_SEQ;
} else {
temp = ftp_session_ptr->NEXT_SEQ;
} /* Endif */
chksum = NAT_chksum_mod(chksum, /* Old checksum */
(uint_16)(ack & 0xFFFF), /* Old data */
(uint_16)(temp &0xFFFF)); /* New data */
chksum = NAT_chksum_mod(chksum, /* Old checksum */
(uint_16)((ack >> 16) & 0xFFFF), /* Old data */
(uint_16)((temp >> 16) & 0xFFFF)); /* New data */
htonl(transport.TCP_PTR->ack, temp); /* Replace old ACK with new one */
htons(transport.TCP_PTR->checksum, chksum);
} /* Endif */
return RTCS_OK;
} /* Endif */
seq_adj = 0;
seq = ntohl(transport.TCP_PTR->seq);
chksum = ntohs(transport.TCP_PTR->checksum);
/* We can only reach this point is the dest port is FTP command port */
if (ftp_session_ptr) {
/* If SEQ > NEXT_SEQ:
**
** We have received an out of order packet. We are expecting a packet with
** SEQ == NEXT_SEQ, but we got a higher SEQ number than expected. The adj
** to the current packets SEQ number is unknown, and it depends on the
** packet with SEQ == NEXT_SEQ (it may or may not be a PORT command). We
** can't do anything until we get the previous packet, so we discard and
** let TCP retransmit it later.
*/
if ((int_32)(seq - ftp_session_ptr->NEXT_SEQ) > 0) {
return (uint_32)RTCS_ERROR;
} else
/* If SEQ < LAST_SEQ:
**
** This is a retransmitted packet or one that took a slow path to the
** NAT gateway. The outside host has already ACKed this packet, and the
** internal host has already properly received the ACK because it has
** sent the next FTP command.
*/
if ((int_32)(seq - ftp_session_ptr->LAST_SEQ) < 0) {
return (uint_32)RTCS_ERROR;
} else
/* If SEQ == NEXT_SEQ:
**
** This is a new packet. Normal scenario.
*/
if (seq == ftp_session_ptr->NEXT_SEQ) {
seq_adj = ftp_session_ptr->NEXT_ADJ;
if (orig_tcp_data_length != 0) {
/* Only update if packet is not a pure ACK with no data */
ftp_session_ptr->LAST_SEQ = seq;
ftp_session_ptr->LAST_ADJ = seq_adj;
ftp_session_ptr->NEXT_SEQ = seq + orig_tcp_data_length;
} /* Endif */
} else /* Endif */
/* IF SEQ == LAST_SEQ:
**
** The last command sent is being retransmitted. Let it through, and do
** any neccessary PORT command modifications
*/
if (seq == ftp_session_ptr->LAST_SEQ) {
seq_adj = ftp_session_ptr->LAST_ADJ;
}
/* This is when the SEQ is > LAST but < NEXT */
else {
return (uint_32)RTCS_ERROR; /* Rebuild? */
} /* Endif */
temp = seq + seq_adj;
/* Write new SEQ number to packet */
chksum = NAT_chksum_mod(chksum,
(uint_16)(seq & 0xFFFF),
(uint_16)(temp & 0xFFFF));
chksum = NAT_chksum_mod(chksum,
(uint_16)((seq >> 16) & 0xFFFF),
(uint_16)((temp >> 16) & 0xFFFF));
htonl(transport.TCP_PTR->seq, temp);
htons(transport.TCP_PTR->checksum, chksum);
} /* Endif */
if (orig_tcp_data_length < 4) {
return RTCS_OK;
} /* Endif */
/* We send the packet through if we don't have a PORT command */
if (memcmp(tcp_data_ptr, "PORT", 4)) {
return RTCS_OK;
} /* Endif */
if (!NAT_ALG_detect_port(tcp_data_ptr, orig_tcp_data_length, &addr, &port)) {
/* This is bad. We only have part of a port command. Toss packet */
return (uint_32)RTCS_ERROR;
} /* Endif */
if (!ftp_session_ptr) {
ftp_session_ptr = RTCS_part_alloc(((NAT_FTP_STRUCT_PTR)(alg_cfg_ptr))->FTP_PART_ID);
if (!ftp_session_ptr) {
return RTCSERR_OUT_OF_MEMORY;
} /* Endif */
/* Attach to session */
ftp_session_ptr->HEADER.ALG_TYPE = NAT_ALG_FTP_TYPE;
ftp_session_ptr->HEADER.NEXT = nat_session_ptr->ALG_INFO_PTR;
nat_session_ptr->ALG_INFO_PTR = (NAT_SESSION_EXT_STRUCT_PTR)(pointer)ftp_session_ptr;
} /* Endif */
/* Create new session for data channel (or find existing session) */
_mem_copy(ip_header_ptr->DEST, NAT_ALG_FTP_dummy.IP.DEST, 4);
htonl(NAT_ALG_FTP_dummy.IP.SOURCE, addr);
htons(NAT_ALG_FTP_dummy.TCP.source_port, port);
htons(NAT_ALG_FTP_dummy.TCP.dest_port, IPPORT_FTPDATA);
nat_data_session_ptr = NAT_lookup(&NAT_ALG_FTP_dummy.IP, FALSE, FALSE, &error);
if (nat_data_session_ptr == NULL) {
nat_data_session_ptr = NAT_insert(&NAT_ALG_FTP_dummy.IP, &error);
if (!nat_data_session_ptr) {
return error;
} /* Endif */
NAT_event_add(&nat_cfg_ptr->TCP_TOUT, &nat_data_session_ptr->TIMEOUT);
} /* Endif */
/* Determine space needed for new PORT information */
new_tcp_data_length = 5; /* "PORT " */
new_tcp_data_length += 7; /* 5 commas and 2 termination bytes */
addr = nat_data_session_ptr->NAT_HST;
port = nat_data_session_ptr->NAT_PORT;
/* Add IP addr len */
for (i = 0; i < 32; i+=8) {
temp = ((addr >> i) & 0xFF);
new_tcp_data_length += (temp < 10) ? 1 : ((temp < 100) ? 2 : 3);
} /* Endfor */
/* Add port len */
for (i = 0; i < 16; i+=8) {
temp = ((port >> i) & 0xFF);
new_tcp_data_length += (temp < 10) ? 1 : ((temp < 100) ? 2 : 3);
} /* Endif */
new_ip_length = ip_header_length + tcp_header_length + new_tcp_data_length;
if (new_ip_length <= orig_ip_length) {
RTCSPCB_shrink(*pcb_ptr_ptr, orig_ip_length - new_ip_length);
} else {
new_mqx_pcb_ptr = _mem_alloc_system(sizeof(PCB) + sizeof(PCB_FRAGMENT) + new_ip_length);
if (!new_mqx_pcb_ptr) {
return RTCSERR_OUT_OF_MEMORY;
} /* Endif */
new_pcb_data_ptr = (uchar_ptr)new_mqx_pcb_ptr + sizeof(PCB) + sizeof(PCB_FRAGMENT);
new_mqx_pcb_ptr->FREE = NAT_ALG_ftp_pcb_free;
new_mqx_pcb_ptr->PRIVATE = NULL;
new_mqx_pcb_ptr->FRAG[0].LENGTH = new_ip_length;
new_mqx_pcb_ptr->FRAG[0].FRAGMENT = new_pcb_data_ptr;
((PCB_FRAGMENT_PTR)(pointer)(new_mqx_pcb_ptr + 1))->LENGTH = 0;
((PCB_FRAGMENT_PTR)(pointer)(new_mqx_pcb_ptr + 1))->FRAGMENT = NULL;
new_pcb_ptr = RTCSPCB_alloc_recv(new_mqx_pcb_ptr);
if (!new_pcb_ptr) {
_mem_free(new_mqx_pcb_ptr);
return RTCSERR_OUT_OF_MEMORY;
} /* Endif */
RTCSPCB_DATA_NETWORK(new_pcb_ptr) = RTCSPCB_DATA(new_pcb_ptr);
new_pcb_ptr->TYPE = (*pcb_ptr_ptr)->TYPE;
new_pcb_ptr->IFSRC = (*pcb_ptr_ptr)->IFSRC;
new_pcb_ptr->LINK_OPTIONS = (*pcb_ptr_ptr)->LINK_OPTIONS;
new_pcb_ptr->IP_COMPLETE = IP_complete_recv;
_mem_copy(ip_header_ptr, new_pcb_data_ptr, ip_header_length + tcp_header_length);
/*
** Update all pointers to the new data
*/
new_pcb_data_ptr = (pointer)RTCSPCB_DATA(new_pcb_ptr);
ip_header_ptr = (pointer)new_pcb_data_ptr;
transport.PTR = TRANSPORT_PTR(ip_header_ptr);
tcp_data_ptr = &transport.DATA_OFFSET[tcp_header_length];
RTCSPCB_free(*pcb_ptr_ptr);
*pcb_ptr_ptr = new_pcb_ptr;
} /* Endif */
/* Rewrite data */
_mem_copy("PORT ", tcp_data_ptr, 5);
tcp_data_ptr += 5;
for (i = 24; i >= 0; i-=8) {
temp = ((addr >> i) & 0xFF);
NAT_ALG_ftp_bwrite((uchar)temp, &tcp_data_ptr);
*tcp_data_ptr++ = ',';
} /* Endfor */
temp = ((port >> 8) & 0xFF);
NAT_ALG_ftp_bwrite((uchar)temp, &tcp_data_ptr);
*tcp_data_ptr++ = ',';
temp = port & 0xFF;
NAT_ALG_ftp_bwrite((uchar)temp, &tcp_data_ptr);
*tcp_data_ptr++ = '\r';
*tcp_data_ptr++ = '\n';
ftp_session_ptr->LAST_SEQ = seq;
ftp_session_ptr->LAST_ADJ = seq_adj;
ftp_session_ptr->NEXT_SEQ = seq + orig_tcp_data_length;
ftp_session_ptr->NEXT_ADJ = seq_adj - orig_tcp_data_length + new_tcp_data_length;
/* Update IP length */
chksum = ntohs(ip_header_ptr->CHECKSUM);
chksum = NAT_chksum_mod(chksum, orig_ip_length, new_ip_length);
htons(ip_header_ptr->CHECKSUM, chksum);
htons(ip_header_ptr->LENGTH, new_ip_length);
/* Calculate TCP checksum */
NAT_ALG_TCP_checksum(ip_header_ptr);
return RTCS_OK;
} /* Endbody */
#endif