VirtualBox

Changeset 41970 in vbox


Ignore:
Timestamp:
Jun 29, 2012 5:55:17 AM (13 years ago)
Author:
vboxsync
Message:

NAT:tftp enhancements style, option parsing, handshaking.

Location:
trunk/src/VBox/Devices/Network/slirp
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/slirp/ctl.h

    r40286 r41970  
    2525#define CTL_BROADCAST   255
    2626
    27 #define CTL_CHECK(x, ctl) ((RT_N2H_U32((x)) & ~pData->netmask) == (ctl) \
     27#define CTL_CHECK(x, ctl) (   ((RT_N2H_U32((x)) & ~pData->netmask) == (ctl)) \
    2828                           && (((x) & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr))
  • trunk/src/VBox/Devices/Network/slirp/slirp_state.h

    r41453 r41970  
    4848
    4949/** TFTP session entry. */
    50 struct tftp_session
    51 {
    52     int in_use;
    53     unsigned char filename[TFTP_FILENAME_MAX];
    54 
    55     struct in_addr client_ip;
    56     u_int16_t client_port;
    57 
    58     int timestamp;
    59     uint64_t u64Offset;
    60 };
     50typedef enum ENMTFTPSESSIONFMT
     51{
     52    TFTPFMT_NONE = 0,
     53    TFTPFMT_OCTET,
     54    TFTPFMT_NETASCII,
     55    TFTPFMT_MAIL,
     56    TFTPFMT_NOT_FMT = 0xffff
     57} ENMTFTPSESSIONFMT;
     58
     59typedef struct TFTPSESSION
     60{
     61    int         fInUse;
     62    unsigned char pszFilename[TFTP_FILENAME_MAX];
     63    struct      in_addr IpClientAddress;
     64    uint16_t    u16ClientPort;
     65    int         iTimestamp;
     66    ENMTFTPSESSIONFMT enmTftpFmt;
     67    uint16_t    u16BlkSize;
     68    uint16_t    u16TSize;
     69    uint16_t    u16Size;
     70    uint16_t    u16Timeout;
     71} TFTPSESSION, *PTFTPSESSION;
     72
     73typedef const PTFTPSESSION PCTFTPSESSION;
    6174
    6275struct dns_domain_entry
     
    183196    int tcp_reass_overflows;
    184197    /* Stuff from tftp.c */
    185     struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
     198    TFTPSESSION aTftpSessions[TFTP_SESSIONS_MAX];
    186199    const char *tftp_prefix;
    187200    /* Stuff from udp.c */
     
    399412#define tcp_now pData->tcp_now
    400413
    401 #define tftp_sessions pData->tftp_sessions
    402414#define tftp_prefix pData->tftp_prefix
    403415
  • trunk/src/VBox/Devices/Network/slirp/tftp.c

    r40121 r41970  
    4343
    4444#include <slirp.h>
    45 
    46 
    47 static void tftp_session_update(PNATState pData, struct tftp_session *spt)
    48 {
    49     spt->timestamp = curtime;
    50     spt->in_use = 1;
    51 }
    52 
    53 static void tftp_session_terminate(struct tftp_session *spt)
    54 {
    55     spt->in_use = 0;
    56 }
    57 
    58 static int tftp_session_allocate(PNATState pData, struct tftp_t *tp)
    59 {
    60     struct tftp_session *spt;
     45#include <iprt/file.h>
     46#include <iprt/asm-math.h>
     47
     48typedef struct TFTPOPTIONDESC
     49{
     50    const char *pszName;
     51    ENMTFTPSESSIONFMT enmType;
     52    int         cbName;
     53    bool        fHasValue;
     54} TFTPOPTIONDESC, *PTFTPOPTIONDESC;
     55
     56typedef const PTFTPOPTIONDESC PCTFTPOPTIONDESC;
     57static TFTPOPTIONDESC g_TftpTransferFmtDesc[] =
     58{
     59    {"octet", TFTPFMT_OCTET, 5, false}, /* RFC1350 */
     60    {"netascii", TFTPFMT_NETASCII, 8, false}, /* RFC1350 */
     61    {"mail", TFTPFMT_MAIL, 4, false}, /* RFC1350 */
     62};
     63
     64static TFTPOPTIONDESC g_TftpDesc[] =
     65{
     66    {"blksize", TFTPFMT_NOT_FMT, 7, true}, /* RFC2348 */
     67    {"timeout", TFTPFMT_NOT_FMT, 7, true}, /* RFC2349 */
     68    {"tsize", TFTPFMT_NOT_FMT, 5, true}, /* RFC2349 */
     69    {"size", TFTPFMT_NOT_FMT, 4, true}, /* RFC2349 */
     70};
     71
     72static uint16_t g_au16RFC2348TftpSessionBlkSize[] =
     73{
     74    512,
     75    1024,
     76    1428,
     77    2048,
     78    4096,
     79    8192
     80};
     81
     82/**
     83 * This function evaluate file name.
     84 * @param pu8Payload
     85 * @param cbPayload
     86 * @param cbFileName
     87 * @return VINF_SUCCESS -
     88 *         VERR_INVALID_PARAMETER -
     89 */
     90DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PCTFTPSESSION pcTftpSession)
     91{
     92    int cbSessionFilename = 0;
     93    int rc = VINF_SUCCESS;
     94    AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
     95    cbSessionFilename = RTStrNLen((const char *)pcTftpSession->pszFilename, TFTP_FILENAME_MAX);
     96    if (   !RTStrNCmp((const char*)pcTftpSession->pszFilename, "../", 3)
     97        || (pcTftpSession->pszFilename[cbSessionFilename - 1] == '/')
     98        ||  RTStrStr((const char *)pcTftpSession->pszFilename, "/../"))
     99        rc = VERR_FILE_NOT_FOUND;
     100
     101    /* only allow exported prefixes */
     102    if (   RT_SUCCESS(rc)
     103        && !tftp_prefix)
     104        rc = VERR_INTERNAL_ERROR;
     105    LogFlowFuncLeaveRC(rc);
     106    return rc;
     107}
     108
     109/*
     110 * This function returns index of option descriptor in passed descriptor array
     111 * @param piIdxOpt returned index value
     112 * @param paTftpDesc array of known Tftp descriptors
     113 * @param caTftpDesc size of array of tftp descriptors
     114 * @param pszOpt name of option
     115 */
     116DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
     117{
     118    int rc = VINF_SUCCESS;
     119    int idxOption = 0;
     120    AssertReturn(piIdxOpt, VERR_INVALID_PARAMETER);
     121    AssertReturn(paTftpDesc, VERR_INVALID_PARAMETER);
     122    AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
     123    for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
     124    {
     125        if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
     126        {
     127            *piIdxOpt = idxOption;
     128            return rc;
     129        }
     130    }
     131    rc = VERR_NOT_FOUND;
     132    return rc;
     133}
     134
     135/**
     136 * Helper function to look for index of descriptor in transfer format descriptors
     137 * @param piIdxOpt returned value of index
     138 * @param pszOpt name of option
     139 */
     140DECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
     141{
     142    return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
     143}
     144
     145/**
     146 * Helper function to look for index of descriptor in options descriptors
     147 * @param piIdxOpt returned value of index
     148 * @param pszOpt name of option
     149 */
     150DECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
     151{
     152    return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
     153}
     154
     155
     156DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
     157{
     158    int idxOptDesc = 0;
     159    AssertPtrReturn(pszOptionName, false);
     160    AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
     161    AssertReturn(RTStrNLen(pszOptionName,10) < 8, false);
     162    for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
     163    {
     164        if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
     165            return true;
     166    }
     167    for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
     168    {
     169        if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
     170            return true;
     171    }
     172    return false;
     173}
     174
     175/**
     176 * This function returns the tftp transfer mode
     177 * @param pTftpIpHeader header of tftp (includes IP, UDP and TFTP) it's required for validating that buffer comming
     178 *      in pcu8Options is comes right after header.
     179 * @param pcu8Options pointer to options buffer
     180 * @param cbOptions size of the options buffer
     181 */
     182DECLINLINE(char *) tftpOptionMode(PCTFTPIPHDR pTftpIpHeader, const uint8_t *pcu8Options, int cbOptions)
     183{
     184    int idxOptDesc = 0;
     185    AssertPtrReturn(pTftpIpHeader, NULL);
     186    AssertPtrReturn(pcu8Options, NULL);
     187    AssertReturn(cbOptions >= 4, NULL);
     188    /* @todo validate that Mode Option just after filename of TFTP */
     189    for (idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
     190    {
     191        if (!RTStrNICmp(g_TftpTransferFmtDesc[idxOptDesc].pszName, (const char *)pcu8Options, cbOptions))
     192            return (char *)g_TftpTransferFmtDesc[idxOptDesc].pszName;
     193    }
     194    return NULL;
     195}
     196
     197/**
     198 * This helper function that validate if client want to operate in supported by server mode.
     199 * @param pcTftpHeader comulative header (IP, UDP, TFTP)
     200 * @param pcu8Options pointer to the options supposing that pointer points at the mode option
     201 * @param cbOptions size of the options buffer
     202 */
     203DECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
     204{
     205    AssertPtrReturn(pcTftpSession, 0);
     206    return (pcTftpSession->enmTftpFmt == TFTPFMT_OCTET);
     207}
     208
     209
     210DECLINLINE(void) tftpSessionUpdate(PNATState pData, PTFTPSESSION pTftpSession)
     211{
     212    pTftpSession->iTimestamp = curtime;
     213    pTftpSession->fInUse = 1;
     214}
     215
     216DECLINLINE(void) tftpSessionTerminate(PTFTPSESSION pTftpSession)
     217{
     218    pTftpSession->fInUse = 0;
     219}
     220
     221DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
     222{
     223    int rc = VINF_SUCCESS;
     224    char *pszTftpRRQRaw;
     225    int idxTftpRRQRaw = 0;
     226    int cbTftpRRQRaw = 0;
     227    int fWithArg = 0;
     228    int idxOptionArg = 0;
     229    AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
     230    AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
     231    AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
     232    LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
     233    pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
     234    cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_OFFSETOF(TFTPIPHDR, Core);
     235    while(cbTftpRRQRaw)
     236    {
     237        idxTftpRRQRaw = RTStrNLen(pszTftpRRQRaw, 512 - idxTftpRRQRaw) + 1;
     238        if (RTStrNLen((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX) == 0)
     239        {
     240            rc = RTStrCopy((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
     241            if (RT_FAILURE(rc))
     242            {
     243                LogFlowFuncLeaveRC(rc);
     244                AssertRCReturn(rc,rc);
     245            }
     246        }
     247        else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
     248        {
     249            int idxFmt = 0;
     250            rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
     251            if (RT_FAILURE(rc))
     252            {
     253                LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
     254                return VERR_INTERNAL_ERROR;
     255            }
     256            AssertReturn(   g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
     257                         && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
     258            pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
     259        }
     260        else if (fWithArg)
     261        {
     262            if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
     263                rc = RTStrToInt16Full(pszTftpRRQRaw, 0, (int16_t *)&pTftpSession->u16BlkSize);
     264            else if (!RTStrICmp("size", g_TftpDesc[idxOptionArg].pszName))
     265                rc = RTStrToInt16Full(pszTftpRRQRaw, 0, (int16_t *)&pTftpSession->u16Size);
     266            else if (!RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
     267                rc = RTStrToInt16Full(pszTftpRRQRaw, 0, (int16_t *)&pTftpSession->u16TSize);
     268            else if (!RTStrICmp("timeoute", g_TftpDesc[idxOptionArg].pszName))
     269                rc = RTStrToInt16Full(pszTftpRRQRaw, 0, (int16_t *)&pTftpSession->u16Timeout);
     270            else
     271                rc = VERR_INVALID_PARAMETER;
     272            if (RT_FAILURE(rc))
     273            {
     274                LogFlowFuncLeaveRC(rc);
     275                AssertRCReturn(rc,rc);
     276            }
     277            fWithArg = 0;
     278            idxOptionArg = 0;
     279        }
     280        else
     281        {
     282            rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
     283            if (RT_SUCCESS(rc))
     284                fWithArg = 1;
     285            else
     286            {
     287                LogFlowFuncLeaveRC(rc);
     288                AssertRCReturn(rc,rc);
     289            }
     290        }
     291        pszTftpRRQRaw += idxTftpRRQRaw;
     292        cbTftpRRQRaw -= idxTftpRRQRaw;
     293    }
     294
     295    LogFlowFuncLeaveRC(rc);
     296    return rc;
     297}
     298
     299static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader)
     300{
     301    PTFTPSESSION pTftpSession;
     302    int rc = VINF_SUCCESS;
     303    int idxSession;
     304
     305    for (idxSession = 0; idxSession < TFTP_SESSIONS_MAX; idxSession++)
     306    {
     307        pTftpSession = &pData->aTftpSessions[idxSession];
     308
     309        if (!pTftpSession->fInUse)
     310            goto found;
     311
     312        /* sessions time out after 5 inactive seconds */
     313        if ((int)(curtime - pTftpSession->iTimestamp) > 5000)
     314            goto found;
     315    }
     316
     317    return -1;
     318
     319 found:
     320    memset(pTftpSession, 0, sizeof(*pTftpSession));
     321    memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
     322    pTftpSession->u16ClientPort = pcTftpIpHeader->UdpHdr.uh_sport;
     323    rc = tftpSessionOptionParse(pTftpSession, pcTftpIpHeader);
     324    AssertRCReturn(rc, -1);
     325
     326    tftpSessionUpdate(pData, pTftpSession);
     327
     328    return idxSession;
     329}
     330
     331static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader)
     332{
     333    PTFTPSESSION pTftpSession;
    61334    int k;
    62335
    63336    for (k = 0; k < TFTP_SESSIONS_MAX; k++)
    64337    {
    65         spt = &tftp_sessions[k];
    66 
    67         if (!spt->in_use)
    68             goto found;
    69 
    70         /* sessions time out after 5 inactive seconds */
    71         if ((int)(curtime - spt->timestamp) > 5000)
    72             goto found;
    73     }
    74 
    75     return -1;
    76 
    77  found:
    78     memset(spt, 0, sizeof(*spt));
    79     memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
    80     spt->client_port = tp->udp.uh_sport;
    81 
    82     tftp_session_update(pData, spt);
    83 
    84     return k;
    85 }
    86 
    87 static int tftp_session_find(PNATState pData, struct tftp_t *tp)
    88 {
    89     struct tftp_session *spt;
    90     int k;
    91 
    92     for (k = 0; k < TFTP_SESSIONS_MAX; k++)
    93     {
    94         spt = &tftp_sessions[k];
    95 
    96         if (spt->in_use)
    97         {
    98             if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)))
     338        pTftpSession = &pData->aTftpSessions[k];
     339
     340        if (pTftpSession->fInUse)
     341        {
     342            if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
    99343            {
    100                 if (spt->client_port == tp->udp.uh_sport)
     344                if (pTftpSession->u16ClientPort == pcTftpIpHeader->UdpHdr.uh_sport)
    101345                    return k;
    102346            }
     
    107351}
    108352
    109 static int tftp_read_data(PNATState pData, struct tftp_session *spt, u_int16_t block_nr,
    110                           u_int8_t *buf, int len)
    111 {
    112     int fd;
    113     int bytes_read = 0;
    114     char buffer[1024];
    115     int n;
    116 
    117     n = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
    118                     tftp_prefix, spt->filename);
    119     if (n >= sizeof(buffer))
     353DECLINLINE(int) pftpSessionOpenFile(PNATState pData, PTFTPSESSION pTftpSession, PRTFILE pSessionFile)
     354{
     355    char aszSessionFileName[TFTP_FILENAME_MAX];
     356    int cbSessionFileName;
     357    int rc = VINF_SUCCESS;
     358    cbSessionFileName = RTStrPrintf(aszSessionFileName, TFTP_FILENAME_MAX, "%s/%s",
     359                    tftp_prefix, pTftpSession->pszFilename);
     360    if (cbSessionFileName >= TFTP_FILENAME_MAX)
     361    {
     362        LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
     363        return VERR_INTERNAL_ERROR;
     364    }
     365
     366    if (!RTFileExists(aszSessionFileName))
     367    {
     368        LogFlowFuncLeaveRC(VERR_FILE_NOT_FOUND);
     369        return VERR_FILE_NOT_FOUND;
     370    }
     371
     372    rc = RTFileOpen(pSessionFile, aszSessionFileName, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
     373    LogFlowFuncLeaveRC(rc);
     374    return rc;
     375}
     376
     377/* @todo: rewrite this */
     378DECLINLINE(int) tftpSessionEvaluateBlkSize(PNATState pData, PTFTPSESSION pTftpSession)
     379{
     380    int rc = VINF_SUCCESS;
     381    RTFILE hSessionFile;
     382    uint64_t cbSessionFile = 0;
     383    int      idxRFC2348TftpSessionBlkSize = 0;
     384    uint32_t cBlockSessionFile = 0;
     385    LogFlowFunc(("pTftpSession:%p\n", pTftpSession));
     386
     387    rc = pftpSessionOpenFile(pData, pTftpSession, &hSessionFile);
     388    if (RT_FAILURE(rc))
     389    {
     390        LogFlowFuncLeave();
     391        return rc;
     392    }
     393
     394    rc = RTFileGetSize(hSessionFile, &cbSessionFile);
     395    RTFileClose(hSessionFile);
     396    if (RT_FAILURE(rc))
     397    {
     398        LogFlowFuncLeave();
     399        return rc;
     400    }
     401
     402    if (!pTftpSession->u16BlkSize)
     403    {
     404        pTftpSession->u16BlkSize = 1428;
     405    }
     406    cBlockSessionFile = ASMDivU64ByU32RetU32(cbSessionFile, pTftpSession->u16BlkSize);
     407    while (   cBlockSessionFile >= UINT16_MAX
     408           && idxRFC2348TftpSessionBlkSize <= RT_ELEMENTS(g_au16RFC2348TftpSessionBlkSize))
     409    {
     410        if (pTftpSession->u16BlkSize > g_au16RFC2348TftpSessionBlkSize[idxRFC2348TftpSessionBlkSize])
     411        {
     412            idxRFC2348TftpSessionBlkSize++;
     413            continue;
     414        }
     415
     416
     417        idxRFC2348TftpSessionBlkSize++;
     418        /* No bigger values in RFC2348 */
     419        AssertReturn(idxRFC2348TftpSessionBlkSize <= RT_ELEMENTS(g_au16RFC2348TftpSessionBlkSize), VERR_INTERNAL_ERROR);
     420        if (g_au16RFC2348TftpSessionBlkSize[idxRFC2348TftpSessionBlkSize] >= if_maxlinkhdr)
     421        {
     422            /* Buffer size is too large for current settings */
     423            rc = VERR_BUFFER_OVERFLOW;
     424            LogFlowFuncLeaveRC(rc);
     425        }
     426    }
     427    LogFlowFuncLeaveRC(rc);
     428    return rc;
     429}
     430
     431DECLINLINE(int) tftpSend(PNATState pData,
     432                         PTFTPSESSION pTftpSession,
     433                         struct mbuf *pMBuf,
     434                         PCTFTPIPHDR pcTftpIpHeaderRecv)
     435{
     436    int rc = VINF_SUCCESS;
     437    struct sockaddr_in saddr, daddr;
     438    LogFlowFunc(("pMBuf:%p, pcTftpIpHeaderRecv:%p\n", pMBuf, pcTftpIpHeaderRecv));
     439    saddr.sin_addr = pcTftpIpHeaderRecv->IPv4Hdr.ip_dst;
     440    saddr.sin_port = pcTftpIpHeaderRecv->UdpHdr.uh_dport;
     441
     442    daddr.sin_addr = pTftpSession->IpClientAddress;
     443    daddr.sin_port = pTftpSession->u16ClientPort;
     444
     445
     446    pMBuf->m_data += sizeof(struct udpiphdr);
     447    pMBuf->m_len -= sizeof(struct udpiphdr);
     448    udp_output2(pData, NULL, pMBuf, &saddr, &daddr, IPTOS_LOWDELAY);
     449    LogFlowFuncLeaveRC(rc);
     450    return rc;
     451}
     452DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode, const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv);
     453
     454DECLINLINE(int) tftpReadDataBlock(PNATState pData,
     455                                  PTFTPSESSION pTftpSession,
     456                                  uint16_t u16BlockNr,
     457                                  uint8_t *pu8Data,
     458                                  int *pcbReadData)
     459{
     460    RTFILE  hSessionFile;
     461    int rc = VINF_SUCCESS;
     462    LogFlowFunc(("pTftpSession:%p, u16BlockNr:%RX16, pu8Data:%p, pcbReadData:%p\n",
     463                    pTftpSession,
     464                    u16BlockNr,
     465                    pu8Data,
     466                    pcbReadData));
     467
     468    rc = pftpSessionOpenFile(pData, pTftpSession, &hSessionFile);
     469    if (RT_FAILURE(rc))
     470    {
     471        LogFlowFuncLeaveRC(rc);
     472        return rc;
     473    }
     474
     475    if (pcbReadData)
     476    {
     477        rc = RTFileSeek(hSessionFile,
     478                        u16BlockNr * pTftpSession->u16BlkSize,
     479                        RTFILE_SEEK_BEGIN,
     480                        NULL);
     481        if (RT_FAILURE(rc))
     482        {
     483            RTFileClose(hSessionFile);
     484            LogFlowFuncLeaveRC(rc);
     485            return rc;
     486        }
     487        rc = RTFileRead(hSessionFile, pu8Data, pTftpSession->u16BlkSize, (size_t *)pcbReadData);
     488        if (RT_FAILURE(rc))
     489        {
     490            RTFileClose(hSessionFile);
     491            LogFlowFuncLeaveRC(rc);
     492            return rc;
     493        }
     494    }
     495
     496    rc = RTFileClose(hSessionFile);
     497
     498    LogFlowFuncLeaveRC(rc);
     499    return rc;
     500}
     501
     502DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint16_t u16OptValue)
     503{
     504    char aszOptionBuffer[256];
     505    int iOptLength = 0;
     506    int rc = VINF_SUCCESS;
     507    int cbMBufCurrent = pMBuf->m_len;
     508    LogFlowFunc(("pMBuf:%p, pszOptName:%s, u16OptValue:%u\n", pMBuf, pszOptName, u16OptValue));
     509    AssertPtrReturn(pMBuf, VERR_INVALID_PARAMETER);
     510    AssertPtrReturn(pszOptName, VERR_INVALID_PARAMETER);
     511
     512    RT_ZERO(aszOptionBuffer);
     513    iOptLength += RTStrPrintf(aszOptionBuffer, 256 , "%s", pszOptName) + 1;
     514    iOptLength += RTStrPrintf(aszOptionBuffer + iOptLength, 256 - iOptLength , "%u", u16OptValue) + 1;
     515    if (iOptLength > M_TRAILINGSPACE(pMBuf))
     516        rc = VERR_BUFFER_OVERFLOW; /* buffer too small */
     517    else
     518    {
     519        pMBuf->m_len += iOptLength;
     520        m_copyback(pData, pMBuf, cbMBufCurrent, iOptLength, aszOptionBuffer);
     521    }
     522    LogFlowFuncLeaveRC(rc);
     523    return rc;
     524}
     525
     526DECLINLINE(int) tftpSendOACK(PNATState pData,
     527                          PTFTPSESSION pTftpSession,
     528                          PCTFTPIPHDR pcTftpIpHeaderRecv)
     529{
     530    struct mbuf *m;
     531    PTFTPIPHDR pTftpIpHeader;
     532    int rc = VINF_SUCCESS;
     533
     534    rc = tftpSessionEvaluateBlkSize(pData, pTftpSession);
     535    if (RT_FAILURE(rc))
     536    {
     537        tftpSendError(pData, pTftpSession, 2, "Internal Error (blksize evaluation)", pcTftpIpHeaderRecv);
     538        LogFlowFuncLeave();
    120539        return -1;
    121 
    122     fd = open(buffer, O_RDONLY | O_BINARY);
    123     if (fd < 0)
    124         return -1;
    125 
    126     if (len)
    127     {
    128         lseek(fd, block_nr * 512, SEEK_SET);
    129         bytes_read = read(fd, buf, len);
    130     }
    131 
    132     close(fd);
    133 
    134     return bytes_read;
    135 }
    136 
    137 static int tftp_send_oack(PNATState pData,
    138                           struct tftp_session *spt,
    139                           const char *key, uint32_t value,
    140                           struct tftp_t *recv_tp)
    141 {
    142     struct sockaddr_in saddr, daddr;
    143     struct mbuf *m;
    144     struct tftp_t *tp;
    145     int n = 0;
     540    }
    146541
    147542    m = slirpTftpMbufAlloc(pData);
     
    149544        return -1;
    150545
     546
     547
    151548    m->m_data += if_maxlinkhdr;
    152549    m->m_pkthdr.header = mtod(m, void *);
    153     tp = (void *)m->m_data;
    154     m->m_data += sizeof(struct udpiphdr);
    155 
    156     tp->tp_op = RT_H2N_U16_C(TFTP_OACK);
    157     n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%s", key) + 1;
    158     n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%u", value) + 1;
    159 
    160     saddr.sin_addr = recv_tp->ip.ip_dst;
    161     saddr.sin_port = recv_tp->udp.uh_dport;
    162 
    163     daddr.sin_addr = spt->client_ip;
    164     daddr.sin_port = spt->client_port;
    165 
    166     m->m_len = sizeof(struct tftp_t) - 514 + n -
    167         sizeof(struct ip) - sizeof(struct udphdr);
    168     udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
    169 
    170     return 0;
    171 }
    172 
    173 static int tftp_send_error(PNATState pData,
    174                            struct tftp_session *spt,
    175                            u_int16_t errorcode, const char *msg,
    176                            struct tftp_t *recv_tp)
    177 {
    178     struct sockaddr_in saddr, daddr;
    179     struct mbuf *m;
    180     struct tftp_t *tp;
     550    pTftpIpHeader = mtod(m, PTFTPIPHDR);
     551    m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */
     552
     553    pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK);
     554
     555    if (pTftpSession->u16BlkSize)
     556        rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->u16BlkSize);
     557    if (   RT_SUCCESS(rc)
     558        && pTftpSession->u16Size)
     559        rc = tftpAddOptionToOACK(pData, m, "size", pTftpSession->u16Size);
     560    if (   RT_SUCCESS(rc)
     561        && pTftpSession->u16TSize)
     562        rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->u16TSize);
     563
     564    rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
     565    return RT_SUCCESS(rc) ? 0 : -1;
     566}
     567
     568DECLINLINE(int) tftpSendError(PNATState pData,
     569                              PTFTPSESSION pTftpSession,
     570                              uint16_t errorcode,
     571                              const char *msg,
     572                              PCTFTPIPHDR pcTftpIpHeaderRecv)
     573{
     574    struct mbuf *m = NULL;
     575    PTFTPIPHDR pTftpIpHeader = NULL;
    181576
    182577    m = slirpTftpMbufAlloc(pData);
     
    185580
    186581    m->m_data += if_maxlinkhdr;
     582    m->m_len = sizeof(TFTPIPHDR)
     583             + strlen(msg) + 1; /* ending zero */
    187584    m->m_pkthdr.header = mtod(m, void *);
    188     tp = (void *)m->m_data;
    189     m->m_data += sizeof(struct udpiphdr);
    190 
    191     tp->tp_op = RT_H2N_U16_C(TFTP_ERROR);
    192     tp->x.tp_error.tp_error_code = RT_H2N_U16(errorcode);
    193     strcpy((char *)tp->x.tp_error.tp_msg, msg);
    194 
    195     saddr.sin_addr = recv_tp->ip.ip_dst;
    196     saddr.sin_port = recv_tp->udp.uh_dport;
    197 
    198     daddr.sin_addr = spt->client_ip;
    199     daddr.sin_port = spt->client_port;
    200 
    201     m->m_len = sizeof(struct tftp_t)
    202              - 514
    203              + 3
    204              + strlen(msg)
    205              - sizeof(struct ip)
    206              - sizeof(struct udphdr);
    207 
    208     udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
    209 
    210     tftp_session_terminate(spt);
     585    pTftpIpHeader = mtod(m, PTFTPIPHDR);
     586
     587    pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
     588    pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);
     589
     590    m_copyback(pData, m, sizeof(TFTPIPHDR), strlen(msg) + 1 /* copy ending zerro*/, (c_caddr_t)msg);
     591
     592    tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
     593
     594    tftpSessionTerminate(pTftpSession);
    211595
    212596    return 0;
    213597}
    214598
    215 static int tftp_send_data(PNATState pData,
    216                           struct tftp_session *spt,
     599static int tftpSendData(PNATState pData,
     600                          PTFTPSESSION pTftpSession,
    217601                          u_int16_t block_nr,
    218                           struct tftp_t *recv_tp)
    219 {
    220     struct sockaddr_in saddr, daddr;
     602                          PCTFTPIPHDR pcTftpIpHeaderRecv)
     603{
    221604    struct mbuf *m;
    222     struct tftp_t *tp;
     605    PTFTPIPHDR pTftpIpHeader;
    223606    int nobytes;
    224 
     607    int rc = VINF_SUCCESS;
     608
     609    /* we should be sure that we don't talk about file offset prior 0 ;) */
    225610    if (block_nr < 1)
    226611        return -1;
     
    232617    m->m_data += if_maxlinkhdr;
    233618    m->m_pkthdr.header = mtod(m, void *);
    234     tp = mtod(m, void *);
    235     m->m_data += sizeof(struct udpiphdr);
    236 
    237     tp->tp_op = RT_H2N_U16_C(TFTP_DATA);
    238     tp->x.tp_data.tp_block_nr = RT_H2N_U16(block_nr);
    239 
    240     saddr.sin_addr = recv_tp->ip.ip_dst;
    241     saddr.sin_port = recv_tp->udp.uh_dport;
    242 
    243     daddr.sin_addr = spt->client_ip;
    244     daddr.sin_port = spt->client_port;
    245 
    246     nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
    247     if (nobytes < 0)
     619    pTftpIpHeader = mtod(m, PTFTPIPHDR);
     620    m->m_len = sizeof(TFTPIPHDR);
     621
     622    pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
     623    pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(block_nr);
     624
     625    rc = tftpReadDataBlock(pData, pTftpSession, block_nr - 1, (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t), &nobytes);
     626
     627    if (RT_SUCCESS(rc))
     628    {
     629        m->m_len += nobytes;
     630        tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
     631        if (nobytes > 0)
     632            tftpSessionUpdate(pData, pTftpSession);
     633        else
     634            tftpSessionTerminate(pTftpSession);
     635    }
     636    else
    248637    {
    249638        m_freem(pData, m);
     639        tftpSendError(pData, pTftpSession, 1, "File not found", pcTftpIpHeaderRecv);
    250640        /* send "file not found" error back */
    251         tftp_send_error(pData, spt, 1, "File not found", tp);
    252641        return -1;
    253642    }
    254643
    255     m->m_len = sizeof(struct tftp_t)
    256              - (512 - nobytes)
    257              - sizeof(struct ip)
    258              - sizeof(struct udphdr);
    259 
    260     udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
    261 
    262     if (nobytes == 512)
    263         tftp_session_update(pData, spt);
    264     else
    265         tftp_session_terminate(spt);
    266 
    267644    return 0;
    268645}
    269646
    270 static void tftp_handle_rrq(PNATState pData, struct tftp_t *tp, int pktlen)
    271 {
    272     struct tftp_session *spt;
    273     int s, k, n;
    274     u_int8_t *src, *dst;
    275 
    276     s = tftp_session_allocate(pData, tp);
     647DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen)
     648{
     649    PTFTPSESSION pTftpSession;
     650    int idxTftpSession = 0;
     651    uint8_t *pu8Payload = NULL;
     652    int     cbPayload = 0;
     653    int cbFileName = 0;
     654
     655    AssertPtrReturnVoid(pTftpIpHeader);
     656    AssertPtrReturnVoid(pData);
     657    AssertReturnVoid(pktlen > sizeof(TFTPIPHDR));
     658    LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen));
     659
     660    idxTftpSession = tftpAllocateSession(pData, pTftpIpHeader);
     661    if (idxTftpSession < 0)
     662    {
     663        LogFlowFuncLeave();
     664        return;
     665    }
     666
     667    pTftpSession = &pData->aTftpSessions[idxTftpSession];
     668    pu8Payload = (uint8_t *)&pTftpIpHeader->Core;
     669    cbPayload = pktlen - sizeof(TFTPIPHDR);
     670
     671    cbFileName = RTStrNLen((char *)pu8Payload, cbPayload);
     672    /* We assume that file name should finish with '\0' and shouldn't bigger
     673     *  than buffer for name storage.
     674     */
     675    AssertReturnVoid(   cbFileName < cbPayload
     676                     && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */
     677                     && cbFileName);
     678
     679    /* Dont't bother with rest processing in case of invalid access */
     680    if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession)))
     681    {
     682        tftpSendError(pData, pTftpSession, 2, "Access violation", pTftpIpHeader);
     683        LogFlowFuncLeave();
     684        return;
     685    }
     686
     687
     688
     689    if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession)))
     690    {
     691        tftpSendError(pData, pTftpSession, 4, "Unsupported transfer mode", pTftpIpHeader);
     692        LogFlowFuncLeave();
     693        return;
     694    }
     695
     696
     697    tftpSendOACK(pData, pTftpSession, pTftpIpHeader);
     698    LogFlowFuncLeave();
     699    return;
     700}
     701
     702static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader)
     703{
     704    int s;
     705
     706    s = tftpSessionFind(pData, pTftpIpHeader);
    277707    if (s < 0)
    278708        return;
    279709
    280     spt = &tftp_sessions[s];
    281 
    282     src = tp->x.tp_buf;
    283     dst = spt->filename;
    284     n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
    285 
    286     /* get name */
    287     for (k = 0; k < n; k++)
    288     {
    289         if (k < TFTP_FILENAME_MAX)
    290             dst[k] = src[k];
    291         else
     710    if (tftpSendData(pData, &pData->aTftpSessions[s],
     711                       RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode) + 1, pTftpIpHeader) < 0)
     712    {
     713        /* XXX */
     714    }
     715}
     716
     717DECLCALLBACK(void) tftp_input(PNATState pData, struct mbuf *pMbuf)
     718{
     719    PTFTPIPHDR pTftpIpHeader = NULL;
     720    AssertPtr(pData);
     721    AssertPtr(pMbuf);
     722    pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR);
     723
     724    switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType))
     725    {
     726        case TFTP_RRQ:
     727            tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL));
     728            break;
     729
     730        case TFTP_ACK:
     731            tftpProcessACK(pData, pTftpIpHeader);
     732            break;
     733        default:
     734            LogFlowFuncLeave();
    292735            return;
    293 
    294         if (src[k] == '\0')
    295             break;
    296     }
    297 
    298     if (k >= n)
    299         return;
    300 
    301     k++;
    302 
    303     /* check mode */
    304     if ((n - k) < 6)
    305         return;
    306 
    307     if (memcmp(&src[k], "octet\0", 6) != 0)
    308     {
    309         tftp_send_error(pData, spt, 4, "Unsupported transfer mode", tp);
    310         return;
    311     }
    312 
    313     k += 6; /* skipping octet */
    314 
    315     /* do sanity checks on the filename */
    316     if (   !strncmp((const char*)spt->filename, "../", 3)
    317         || (spt->filename[strlen((const char *)spt->filename) - 1] == '/')
    318         ||  strstr((const char *)spt->filename, "/../"))
    319     {
    320         tftp_send_error(pData, spt, 2, "Access violation", tp);
    321         return;
    322     }
    323 
    324     /* only allow exported prefixes */
    325     if (!tftp_prefix)
    326     {
    327         tftp_send_error(pData, spt, 2, "Access violation", tp);
    328         return;
    329     }
    330 
    331     /* check if the file exists */
    332     if (tftp_read_data(pData, spt, 0, spt->filename, 0) < 0)
    333     {
    334         tftp_send_error(pData, spt, 1, "File not found", tp);
    335         return;
    336     }
    337 
    338     if (src[n - 1] != 0)
    339     {
    340         tftp_send_error(pData, spt, 2, "Access violation", tp);
    341         return;
    342     }
    343 
    344     while (k < n)
    345     {
    346         const char *key, *value;
    347 
    348         key = (const char *)src + k;
    349         k += strlen(key) + 1;
    350 
    351         if (k >= n)
    352         {
    353             tftp_send_error(pData, spt, 2, "Access violation", tp);
    354             return;
    355         }
    356 
    357         value = (const char *)src + k;
    358         k += strlen(value) + 1;
    359 
    360         if (strcmp(key, "tsize") == 0)
    361         {
    362             int tsize = atoi(value);
    363             struct stat stat_p;
    364 
    365             if (tsize == 0 && tftp_prefix)
    366             {
    367                 char buffer[1024];
    368                 int len;
    369 
    370                 len = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
    371                                   tftp_prefix, spt->filename);
    372                 if (RT_UNLIKELY(len <= 0))
    373                 {
    374                     tftp_send_error(pData, spt, 1, "Filename is invalid", tp);
    375                     return;
    376                 }
    377                 if (stat(buffer, &stat_p) == 0)
    378                     tsize = stat_p.st_size;
    379                 else
    380                 {
    381                     tftp_send_error(pData, spt, 1, "File not found", tp);
    382                     return;
    383                 }
    384             }
    385 
    386             tftp_send_oack(pData, spt, "tsize", tsize, tp);
    387             return;
    388         }
    389     }
    390 
    391     tftp_send_data(pData, spt, 1, tp);
    392 }
    393 
    394 static void tftp_handle_ack(PNATState pData, struct tftp_t *tp)
    395 {
    396     int s;
    397 
    398     s = tftp_session_find(pData, tp);
    399     if (s < 0)
    400         return;
    401 
    402     if (tftp_send_data(pData, &tftp_sessions[s],
    403                        RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0)
    404     {
    405         /* XXX */
    406     }
    407 }
    408 
    409 void tftp_input(PNATState pData, struct mbuf *m)
    410 {
    411     struct tftp_t *tp = (struct tftp_t *)m->m_data;
    412 
    413     switch(RT_N2H_U16(tp->tp_op))
    414     {
    415         case TFTP_RRQ:
    416             tftp_handle_rrq(pData, tp, m->m_len);
    417             break;
    418 
    419         case TFTP_ACK:
    420             tftp_handle_ack(pData, tp);
    421             break;
    422     }
    423 }
     736    }
     737}
  • trunk/src/VBox/Devices/Network/slirp/tftp.h

    r28800 r41970  
    3131#define TFTP_FILENAME_MAX 512
    3232
     33#if 0
    3334struct tftp_t
    3435{
     
    5152    } x;
    5253};
     54#else
     55#pragma pack(0)
     56typedef struct TFTPCOREHDR
     57{
     58    uint16_t    u16TftpOpCode;
     59#if 0
     60    union {
     61        uint16_t u16BlockNum;
     62        uint16_t u16TftpErrorCode;
     63    } X;
     64#endif
     65    /* Data lays here (might be raw uint8_t* or header of payload ) */
     66} TFTPCOREHDR, *PTFTPCOREHDR;
     67
     68typedef struct TFTPIPHDR
     69{
     70    struct ip       IPv4Hdr;
     71    struct udphdr   UdpHdr;
     72    uint16_t        u16TftpOpType;
     73    TFTPCOREHDR     Core;
     74    /* Data lays here */
     75} TFTPIPHDR, *PTFTPIPHDR;
     76#pragma pack()
     77
     78typedef const PTFTPIPHDR PCTFTPIPHDR;
     79#endif
    5380
    5481void tftp_input(PNATState pData, struct mbuf *m);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette