tftp.c revision 832219eeb09c7575b50c68e93c9c745d04d0006a
/* $Id$ */
/** @file
* NAT - TFTP server.
*/
/*
* Copyright (C) 2006-2012 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;
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 -
*/
{
size_t 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 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;
size_t 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)
{
{
}
if ( RT_SUCCESS(rc)
/* @todo: we don't use timeout, but its value in the range 0-255 */
if ( RT_SUCCESS(rc)
/* @todo: unknown option detection */
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 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;
}
{
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, 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, uint64_t u64OptValue)
{
char aszOptionBuffer[256];
size_t iOptLength = 0;
int rc = VINF_SUCCESS;
iOptLength += RTStrPrintf(aszOptionBuffer + iOptLength, 256 - iOptLength , "%llu", u64OptValue) + 1;
else
{
}
return rc;
}
{
struct mbuf *m;
int rc = VINF_SUCCESS;
if (RT_FAILURE(rc))
{
tftpSendError(pData, pTftpSession, 2, "Option negotiation failure (file not found or inaccessible?)", pcTftpIpHeaderRecv);
return -1;
}
m = slirpTftpMbufAlloc(pData);
if (!m)
return -1;
m->m_data += if_maxlinkhdr;
{
else
}
if ( RT_SUCCESS(rc)
}
const char *msg,
{
m = slirpTftpMbufAlloc(pData);
if (!m)
{
LogFlowFunc(("LEAVE: Can't allocate mbuf\n"));
return -1;
}
m->m_data += if_maxlinkhdr;
return 0;
}
{
struct mbuf *m;
int cbRead = 0;
int rc = VINF_SUCCESS;
pTftpSession->cTftpAck++;
else
{
return -1;
}
m = slirpTftpMbufAlloc(pData);
if (!m)
return -1;
m->m_data += if_maxlinkhdr;
rc = tftpReadDataBlock(pData, pTftpSession, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &cbRead);
if (RT_SUCCESS(rc))
{
if (cbRead > 0)
else
}
else
{
/* send "file not found" error back */
return -1;
}
return 0;
}
{
int cbPayload = 0;
size_t 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;
LogRel(("NAT TFTP: failure\n"));
}
{
return VINF_SUCCESS;
}
{
}
{
{
case TFTP_RRQ:
break;
case TFTP_ACK:
break;
default:;
}
return VINF_SUCCESS;
}