tftp.c revision 6daf914ccb7c009e09d4ada481e3871c309f5cc7
/* $Id$ */
/** @file
* NAT - TFTP server.
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*
* This code is based on:
*
* tftp.c - a simple, read-only tftp server for qemu
*
* Copyright (c) 2004 Magnus Damm <damm@opensource.se>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <slirp.h>
#include <iprt/asm-math.h>
typedef enum ENMTFTPSESSIONFMT
{
TFTPFMT_NONE = 0,
TFTPFMT_NOT_FMT = 0xffff
typedef struct TFPTPSESSIONOPTDESC
{
int fRequested;
int u16Value;
typedef struct TFTPSESSION
{
int fInUse;
unsigned char pszFilename[TFTP_FILENAME_MAX];
struct in_addr IpClientAddress;
int iTimestamp;
#pragma pack(1)
typedef struct TFTPCOREHDR
{
/* Data lays here (might be raw uint8_t* or header of payload ) */
} TFTPCOREHDR, *PTFTPCOREHDR;
typedef struct TFTPIPHDR
{
/* Data lays here */
} TFTPIPHDR, *PTFTPIPHDR;
#pragma pack()
typedef const PTFTPIPHDR PCTFTPIPHDR;
typedef const PTFTPSESSION PCTFTPSESSION;
typedef struct TFTPOPTIONDESC
{
const char *pszName;
int cbName;
bool fHasValue;
typedef const PTFTPOPTIONDESC PCTFTPOPTIONDESC;
static TFTPOPTIONDESC g_TftpTransferFmtDesc[] =
{
};
static TFTPOPTIONDESC g_TftpDesc[] =
{
};
/**
* This function evaluate file name.
* @param pu8Payload
* @param cbPayload
* @param cbFileName
* @return VINF_SUCCESS -
* VERR_INVALID_PARAMETER -
*/
{
int cbSessionFilename = 0;
int rc = VINF_SUCCESS;
/* only allow exported prefixes */
if ( RT_SUCCESS(rc)
&& !tftp_prefix)
return rc;
}
/*
* This function returns index of option descriptor in passed descriptor array
* @param piIdxOpt returned index value
* @param paTftpDesc array of known Tftp descriptors
* @param caTftpDesc size of array of tftp descriptors
* @param pszOpt name of option
*/
DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
{
int rc = VINF_SUCCESS;
int idxOption = 0;
{
{
return rc;
}
}
rc = VERR_NOT_FOUND;
return rc;
}
/**
* Helper function to look for index of descriptor in transfer format descriptors
* @param piIdxOpt returned value of index
* @param pszOpt name of option
*/
{
return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
}
/**
* Helper function to look for index of descriptor in options descriptors
* @param piIdxOpt returned value of index
* @param pszOpt name of option
*/
{
}
{
int idxOptDesc = 0;
AssertPtrReturn(pszOptionName, false);
{
return true;
}
{
return true;
}
return false;
}
/**
* This function returns the tftp transfer mode
* @param pTftpIpHeader header of tftp (includes IP, UDP and TFTP) it's required for validating that buffer comming
* in pcu8Options is comes right after header.
* @param pcu8Options pointer to options buffer
* @param cbOptions size of the options buffer
*/
DECLINLINE(char *) tftpOptionMode(PCTFTPIPHDR pTftpIpHeader, const uint8_t *pcu8Options, int cbOptions)
{
int idxOptDesc = 0;
/* @todo validate that Mode Option just after filename of TFTP */
{
}
return NULL;
}
/**
* This helper function that validate if client want to operate in supported by server mode.
* @param pcTftpHeader comulative header (IP, UDP, TFTP)
* @param pcu8Options pointer to the options supposing that pointer points at the mode option
* @param cbOptions size of the options buffer
*/
{
}
{
}
{
pTftpSession->fInUse = 0;
}
DECLINLINE(int)tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
{
int rc = VINF_SUCCESS;
return rc;
}
{
int rc = VINF_SUCCESS;
char *pszTftpRRQRaw;
int idxTftpRRQRaw = 0;
int cbTftpRRQRaw = 0;
int fWithArg = 0;
int idxOptionArg = 0;
cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_OFFSETOF(TFTPIPHDR, Core);
while(cbTftpRRQRaw)
{
{
if (RT_FAILURE(rc))
{
}
}
{
int idxFmt = 0;
if (RT_FAILURE(rc))
{
return VERR_INTERNAL_ERROR;
}
}
else if (fWithArg)
{
else
if (RT_FAILURE(rc))
{
}
fWithArg = 0;
idxOptionArg = 0;
}
else
{
if (RT_SUCCESS(rc))
fWithArg = 1;
else
{
}
}
}
return rc;
}
static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
{
int rc = VINF_SUCCESS;
int idxSession;
{
if (!pTftpSession->fInUse)
goto found;
/* sessions time out after 5 inactive seconds */
goto found;
}
return VERR_NOT_FOUND;
memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
return VINF_SUCCESS;
}
static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSessions)
{
int idxTftpSession;
{
if (pTftpSession->fInUse)
{
if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
{
{
return VINF_SUCCESS;
}
}
}
}
return VERR_NOT_FOUND;
}
DECLINLINE(int) pftpSessionOpenFile(PNATState pData, PTFTPSESSION pTftpSession, PRTFILE pSessionFile)
{
int cbSessionFileName;
int rc = VINF_SUCCESS;
if (cbSessionFileName >= TFTP_FILENAME_MAX)
{
return VERR_INTERNAL_ERROR;
}
if (!RTFileExists(aszSessionFileName))
{
return VERR_FILE_NOT_FOUND;
}
rc = RTFileOpen(pSessionFile, aszSessionFileName, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
return rc;
}
/* @todo: rewrite this */
{
int rc = VINF_SUCCESS;
uint64_t cbSessionFile = 0;
if (RT_FAILURE(rc))
{
return rc;
}
if (RT_FAILURE(rc))
{
return rc;
}
{
}
return rc;
}
{
int rc = VINF_SUCCESS;
return rc;
}
DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode, const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv);
int *pcbReadData)
{
int rc = VINF_SUCCESS;
uint16_t u16BlkSize = 0;
LogFlowFunc(("pcTftpSession:%p, u16BlockNr:%RX16, pu8Data:%p, pcbReadData:%p\n",
pcbReadData));
if (RT_FAILURE(rc))
{
return rc;
}
if (pcbReadData)
{
NULL);
if (RT_FAILURE(rc))
{
return rc;
}
if (RT_FAILURE(rc))
{
return rc;
}
}
return rc;
}
DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint16_t u16OptValue)
{
char aszOptionBuffer[256];
int iOptLength = 0;
int rc = VINF_SUCCESS;
else
{
}
return rc;
}
{
struct mbuf *m;
int rc = VINF_SUCCESS;
if (RT_FAILURE(rc))
{
return -1;
}
m = slirpTftpMbufAlloc(pData);
if (!m)
return -1;
m->m_data += if_maxlinkhdr;
}
const char *msg,
{
m = slirpTftpMbufAlloc(pData);
if (!m)
return -1;
m->m_data += if_maxlinkhdr;
return 0;
}
{
struct mbuf *m;
int nobytes;
int rc = VINF_SUCCESS;
/* we should be sure that we don't talk about file offset prior 0 ;) */
if (block_nr < 1)
return -1;
m = slirpTftpMbufAlloc(pData);
if (!m)
return -1;
m->m_data += if_maxlinkhdr;
rc = tftpReadDataBlock(pData, pTftpSession, block_nr - 1, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &nobytes);
if (RT_SUCCESS(rc))
{
if (nobytes > 0)
else
}
else
{
/* send "file not found" error back */
return -1;
}
return 0;
}
{
int cbPayload = 0;
int cbFileName = 0;
int rc = VINF_SUCCESS;
if ( RT_FAILURE(rc)
|| pTftpSession == NULL)
{
return;
}
/* We assume that file name should finish with '\0' and shouldn't bigger
* than buffer for name storage.
*/
&& cbFileName);
/* Dont't bother with rest processing in case of invalid access */
{
return;
}
{
return;
}
return;
}
{
int rc;
if (RT_FAILURE(rc))
return;
}
{
return VINF_SUCCESS;
}
{
{
case TFTP_RRQ:
break;
case TFTP_ACK:
break;
default:;
}
return VINF_SUCCESS;
}