tftp.c revision cbd264cc4b3be338103895d005a2c3d25b76fd57
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * NAT - TFTP server.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * Copyright (C) 2006-2012 Oracle Corporation
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * This file is part of VirtualBox Open Source Edition (OSE), as
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * available from http://www.virtualbox.org. This file is free software;
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * you can redistribute it and/or modify it under the terms of the GNU
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * General Public License (GPL) as published by the Free Software
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * Foundation, in version 2 as it comes in the "COPYING" file of the
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * This code is based on:
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * tftp.c - a simple, read-only tftp server for qemu
e5dcf7beb7c949f9234713d5818b581ec3825443Robert Johnston * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * Permission is hereby granted, free of charge, to any person obtaining a copy
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * of this software and associated documentation files (the "Software"), to deal
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * in the Software without restriction, including without limitation the rights
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * copies of the Software, and to permit persons to whom the Software is
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * furnished to do so, subject to the following conditions:
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * The above copyright notice and this permission notice shall be included in
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * all copies or substantial portions of the Software.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * THE SOFTWARE.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecinditypedef struct TFTPSESSION
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecinditypedef struct TFTPCOREHDR
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi /* Data lays here (might be raw uint8_t* or header of payload ) */
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecinditypedef struct TFTPIPHDR
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi /* Data lays here */
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecinditypedef struct TFTPOPTIONDESC
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi const char *pszName;
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj {"netascii", TFTPFMT_NETASCII, 8, false}, /* RFC1350 */
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * This function evaluate file name.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param pu8Payload
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param cbPayload
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param cbFileName
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @return VINF_SUCCESS -
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * VERR_INVALID_PARAMETER -
0eb822a1c0c2bea495647510b75f77f0e57633ebcindiDECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PCTFTPSESSION pcTftpSession)
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
825ba0f20a74fd9c5d0d1ce2c195da2cc88a7f77robj cbSessionFilename = RTStrNLen((const char *)pcTftpSession->pszFilename, TFTP_FILENAME_MAX);
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi if ( !RTStrNCmp((const char*)pcTftpSession->pszFilename, "../", 3)
88045cff0aae4ed8823cd0989168e8f56927f83eRobert Johnston || (pcTftpSession->pszFilename[cbSessionFilename - 1] == '/')
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi || RTStrStr((const char *)pcTftpSession->pszFilename, "/../"))
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi /* only allow exported prefixes */
88045cff0aae4ed8823cd0989168e8f56927f83eRobert Johnston * This function returns index of option descriptor in passed descriptor array
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * @param piIdxOpt returned index value
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * @param paTftpDesc array of known Tftp descriptors
825ba0f20a74fd9c5d0d1ce2c195da2cc88a7f77robj * @param caTftpDesc size of array of tftp descriptors
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * @param pszOpt name of option
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robjDECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
88045cff0aae4ed8823cd0989168e8f56927f83eRobert Johnston AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
4557a2a1868181b517f5dfe61ba6eeba58edf4c0robj * Helper function to look for index of descriptor in transfer format descriptors
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * @param piIdxOpt returned value of index
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * @param pszOpt name of option
0eb822a1c0c2bea495647510b75f77f0e57633ebcindiDECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * Helper function to look for index of descriptor in options descriptors
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * @param piIdxOpt returned value of index
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * @param pszOpt name of option
0eb822a1c0c2bea495647510b75f77f0e57633ebcindiDECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
0eb822a1c0c2bea495647510b75f77f0e57633ebcindiDECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi return true;
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi return true;
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi return false;
0eb822a1c0c2bea495647510b75f77f0e57633ebcindi * This helper function that validate if client want to operate in supported by server mode.
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param pcTftpHeader comulative header (IP, UDP, TFTP)
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param pcu8Options pointer to the options supposing that pointer points at the mode option
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindi * @param cbOptions size of the options buffer
7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fecindiDECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
DECLINLINE(int)tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
return rc;
char *pszTftpRRQRaw;
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)
int idxFmt = 0;
return VERR_INTERNAL_ERROR;
else if (fWithArg)
fWithArg = 0;
idxOptionArg = 0;
return rc;
static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
int idxSession;
goto found;
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 (!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)
return VERR_INTERNAL_ERROR;
return VERR_FILE_NOT_FOUND;
rc = RTFileOpen(pSessionFile, aszSessionFileName, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
return rc;
return rc;
return rc;
return rc;
return rc;
DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode, const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv);
int *pcbReadData)
pcbReadData));
return rc;
if (pcbReadData)
NULL);
return rc;
return rc;
return rc;
DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint64_t u64OptValue)
return rc;
struct mbuf *m;
const char *msg,
struct mbuf *m;
int cbRead = 0;
rc = tftpReadDataBlock(pData, pTftpSession, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &cbRead);
if (cbRead > 0)
int cbPayload = 0;
&& cbFileName);
int rc;
return VINF_SUCCESS;
case TFTP_RRQ:
case TFTP_ACK:
return VINF_SUCCESS;