VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/tftp.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.2 KB
RevLine 
[28449]1/* $Id: tftp.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * NAT - TFTP server.
4 */
5
[1]6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[28449]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[28449]26 */
27
28/*
29 * This code is based on:
30 *
[1]31 * tftp.c - a simple, read-only tftp server for qemu
[1033]32 *
[1]33 * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
[1033]34 *
[1]35 * Permission is hereby granted, free of charge, to any person obtaining a copy
36 * of this software and associated documentation files (the "Software"), to deal
37 * in the Software without restriction, including without limitation the rights
38 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39 * copies of the Software, and to permit persons to whom the Software is
40 * furnished to do so, subject to the following conditions:
41 *
42 * The above copyright notice and this permission notice shall be included in
43 * all copies or substantial portions of the Software.
44 *
45 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
48 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51 * THE SOFTWARE.
52 */
53
54#include <slirp.h>
[41970]55#include <iprt/file.h>
[76346]56#include <iprt/err.h>
[76775]57#include <iprt/path.h>
[1]58
[41987]59typedef enum ENMTFTPSESSIONFMT
60{
61 TFTPFMT_NONE = 0,
62 TFTPFMT_OCTET,
63 TFTPFMT_NETASCII,
64 TFTPFMT_MAIL,
65 TFTPFMT_NOT_FMT = 0xffff
66} ENMTFTPSESSIONFMT;
67
[41996]68typedef struct TFPTPSESSIONOPTDESC
69{
70 int fRequested;
[42180]71 uint64_t u64Value;
[41996]72} TFPTPSESSIONOPTDESC, *PTFPTPSESSIONOPTDESC;
73
[41987]74typedef struct TFTPSESSION
75{
76 int fInUse;
77 struct in_addr IpClientAddress;
78 uint16_t u16ClientPort;
79 int iTimestamp;
[42010]80 uint64_t cbTransfered;
[42020]81 uint16_t cTftpAck;
[41987]82 ENMTFTPSESSIONFMT enmTftpFmt;
[41996]83 TFPTPSESSIONOPTDESC OptionBlkSize;
84 TFPTPSESSIONOPTDESC OptionTSize;
85 TFPTPSESSIONOPTDESC OptionTimeout;
[91623]86
[91631]87 const char *pcszFilenameHost;
[91623]88 char szFilename[TFTP_FILENAME_MAX];
[41987]89} TFTPSESSION, *PTFTPSESSION, **PPTFTPSESSION;
90
91#pragma pack(1)
92typedef struct TFTPCOREHDR
93{
94 uint16_t u16TftpOpCode;
95 /* Data lays here (might be raw uint8_t* or header of payload ) */
96} TFTPCOREHDR, *PTFTPCOREHDR;
97
98typedef struct TFTPIPHDR
99{
100 struct ip IPv4Hdr;
101 struct udphdr UdpHdr;
102 uint16_t u16TftpOpType;
103 TFTPCOREHDR Core;
104 /* Data lays here */
105} TFTPIPHDR, *PTFTPIPHDR;
106#pragma pack()
107
108typedef const PTFTPIPHDR PCTFTPIPHDR;
109
110typedef const PTFTPSESSION PCTFTPSESSION;
111
112
[41970]113typedef struct TFTPOPTIONDESC
114{
115 const char *pszName;
116 ENMTFTPSESSIONFMT enmType;
117 int cbName;
118 bool fHasValue;
119} TFTPOPTIONDESC, *PTFTPOPTIONDESC;
[1033]120
[41970]121typedef const PTFTPOPTIONDESC PCTFTPOPTIONDESC;
122static TFTPOPTIONDESC g_TftpTransferFmtDesc[] =
[1]123{
[41970]124 {"octet", TFTPFMT_OCTET, 5, false}, /* RFC1350 */
125 {"netascii", TFTPFMT_NETASCII, 8, false}, /* RFC1350 */
126 {"mail", TFTPFMT_MAIL, 4, false}, /* RFC1350 */
127};
128
129static TFTPOPTIONDESC g_TftpDesc[] =
130{
131 {"blksize", TFTPFMT_NOT_FMT, 7, true}, /* RFC2348 */
132 {"timeout", TFTPFMT_NOT_FMT, 7, true}, /* RFC2349 */
133 {"tsize", TFTPFMT_NOT_FMT, 5, true}, /* RFC2349 */
134 {"size", TFTPFMT_NOT_FMT, 4, true}, /* RFC2349 */
135};
136
[80980]137
138DECLINLINE(struct mbuf *) slirpTftpMbufAlloc(PNATState pData)
139{
140 struct mbuf *m = slirpServiceMbufAlloc(pData, CTL_TFTP);
141 if (RT_UNLIKELY(m == NULL))
142 LogFlowFunc(("LEAVE: Can't allocate mbuf\n"));
143 return m;
144}
145
146
[41970]147/**
[91631]148 * This function resolves file name relative to tftp prefix.
149 * @param pData
150 * @param pTftpSession
[41970]151 */
[91631]152DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PTFTPSESSION pTftpSession)
[41970]153{
[91631]154 int rc = VERR_FILE_NOT_FOUND; /* guilty until proved innocent */
[41970]155
[91631]156 AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
157 AssertReturn(pTftpSession->pcszFilenameHost == NULL, VERR_INVALID_PARAMETER);
[76775]158
[91631]159 /* prefix must be set to an absolute pathname. assert? */
160 if (tftp_prefix == NULL || RTPathSkipRootSpec(tftp_prefix) == tftp_prefix)
161 goto done;
[76775]162
[91631]163 /* replace backslashes with forward slashes */
164 char *s = pTftpSession->szFilename;
165 while ((s = strchr(s, '\\')) != NULL)
166 *s++ = '/';
167
[93228]168 /* deny dot-dot by itself or at the beginning */
169 if ( pTftpSession->szFilename[0] == '.'
170 && pTftpSession->szFilename[1] == '.'
171 && ( pTftpSession->szFilename[2] == '\0'
172 || pTftpSession->szFilename[2] == '/'))
[91631]173 goto done;
174
[93228]175 /* deny dot-dot in the middle */
176 if (RTStrStr(pTftpSession->szFilename, "/../") != NULL)
177 goto done;
178
179 /* deny dot-dot at the end (there's no RTStrEndsWith) */
[91631]180 const char *dotdot = RTStrStr(pTftpSession->szFilename, "/..");
[93228]181 if (dotdot != NULL && dotdot[3] == '\0')
[91631]182 goto done;
183
184 char *pszPathHostAbs;
185 int cbLen = RTStrAPrintf(&pszPathHostAbs, "%s/%s",
186 tftp_prefix, pTftpSession->szFilename);
187 if (cbLen == -1)
188 goto done;
189
190 LogRel2(("NAT: TFTP: %s\n", pszPathHostAbs));
191 pTftpSession->pcszFilenameHost = pszPathHostAbs;
192 rc = VINF_SUCCESS;
193
194 done:
[41970]195 LogFlowFuncLeaveRC(rc);
196 return rc;
[1]197}
198
[41970]199/*
200 * This function returns index of option descriptor in passed descriptor array
201 * @param piIdxOpt returned index value
202 * @param paTftpDesc array of known Tftp descriptors
203 * @param caTftpDesc size of array of tftp descriptors
204 * @param pszOpt name of option
205 */
206DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
[1]207{
[41970]208 int rc = VINF_SUCCESS;
209 int idxOption = 0;
210 AssertReturn(piIdxOpt, VERR_INVALID_PARAMETER);
211 AssertReturn(paTftpDesc, VERR_INVALID_PARAMETER);
212 AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
213 for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
214 {
215 if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
216 {
217 *piIdxOpt = idxOption;
218 return rc;
219 }
220 }
221 rc = VERR_NOT_FOUND;
222 return rc;
[1]223}
224
[41970]225/**
226 * Helper function to look for index of descriptor in transfer format descriptors
227 * @param piIdxOpt returned value of index
228 * @param pszOpt name of option
229 */
230DECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
[1]231{
[41970]232 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
233}
[1]234
[41970]235/**
236 * Helper function to look for index of descriptor in options descriptors
237 * @param piIdxOpt returned value of index
238 * @param pszOpt name of option
239 */
240DECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
241{
242 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
243}
244
245
[63478]246#if 0 /* unused */
[41970]247DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
248{
249 int idxOptDesc = 0;
250 AssertPtrReturn(pszOptionName, false);
251 AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
252 AssertReturn(RTStrNLen(pszOptionName,10) < 8, false);
253 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
[14964]254 {
[41970]255 if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
256 return true;
257 }
258 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
259 {
260 if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
261 return true;
262 }
263 return false;
264}
[63478]265#endif /* unused */
[1]266
[41970]267
268/**
269 * This helper function that validate if client want to operate in supported by server mode.
270 * @param pcTftpHeader comulative header (IP, UDP, TFTP)
271 * @param pcu8Options pointer to the options supposing that pointer points at the mode option
272 * @param cbOptions size of the options buffer
273 */
274DECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
275{
276 AssertPtrReturn(pcTftpSession, 0);
277 return (pcTftpSession->enmTftpFmt == TFTPFMT_OCTET);
278}
279
280
281DECLINLINE(void) tftpSessionUpdate(PNATState pData, PTFTPSESSION pTftpSession)
282{
283 pTftpSession->iTimestamp = curtime;
284 pTftpSession->fInUse = 1;
285}
286
287DECLINLINE(void) tftpSessionTerminate(PTFTPSESSION pTftpSession)
288{
[91631]289 if (pTftpSession->pcszFilenameHost != NULL)
290 {
291 RTStrFree((char *)pTftpSession->pcszFilenameHost);
292 pTftpSession->pcszFilenameHost = NULL;
293 }
294
[41970]295 pTftpSession->fInUse = 0;
296}
297
[42431]298DECLINLINE(int) tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
[41996]299{
300 int rc = VINF_SUCCESS;
[42180]301 rc = RTStrToInt64Full(pcszRawOption, 0, (int64_t *)&pTftpSessionOption->u64Value);
[41996]302 AssertRCReturn(rc, rc);
303 pTftpSessionOption->fRequested = 1;
304 return rc;
305}
306
[41970]307DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
308{
309 int rc = VINF_SUCCESS;
310 char *pszTftpRRQRaw;
[42013]311 size_t idxTftpRRQRaw = 0;
[63014]312 ssize_t cbTftpRRQRaw = 0;
[41970]313 int fWithArg = 0;
314 int idxOptionArg = 0;
[64585]315
[41970]316 AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
317 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
318 AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
319 LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
[64585]320
[41970]321 pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
[73097]322 cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_UOFFSETOF(TFTPIPHDR, Core);
[63014]323 while (cbTftpRRQRaw)
[41970]324 {
[76782]325 rc = RTStrNLenEx(pszTftpRRQRaw, cbTftpRRQRaw, &idxTftpRRQRaw);
326 if (RT_SUCCESS(rc))
327 ++idxTftpRRQRaw; /* count the NUL too */
328 else
329 break;
330
[91623]331 if (RTStrNLen(pTftpSession->szFilename, TFTP_FILENAME_MAX) == 0)
[41970]332 {
[91623]333 rc = RTStrCopy(pTftpSession->szFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
[41970]334 if (RT_FAILURE(rc))
335 {
336 LogFlowFuncLeaveRC(rc);
337 AssertRCReturn(rc,rc);
338 }
339 }
340 else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
341 {
342 int idxFmt = 0;
343 rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
344 if (RT_FAILURE(rc))
345 {
346 LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
347 return VERR_INTERNAL_ERROR;
348 }
349 AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
350 && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
351 pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
352 }
353 else if (fWithArg)
354 {
355 if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
[41996]356 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
[42187]357
358 if ( RT_SUCCESS(rc)
359 && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
[41996]360 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize);
[42187]361
[63562]362 /** @todo we don't use timeout, but its value in the range 0-255 */
[42187]363 if ( RT_SUCCESS(rc)
364 && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName))
[42180]365 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout);
[42187]366
[63562]367 /** @todo unknown option detection */
[41970]368 if (RT_FAILURE(rc))
369 {
370 LogFlowFuncLeaveRC(rc);
371 AssertRCReturn(rc,rc);
372 }
373 fWithArg = 0;
374 idxOptionArg = 0;
375 }
376 else
377 {
378 rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
379 if (RT_SUCCESS(rc))
380 fWithArg = 1;
381 else
382 {
383 LogFlowFuncLeaveRC(rc);
384 AssertRCReturn(rc,rc);
385 }
386 }
387 pszTftpRRQRaw += idxTftpRRQRaw;
[63014]388 cbTftpRRQRaw -= idxTftpRRQRaw;
[41970]389 }
390
391 LogFlowFuncLeaveRC(rc);
392 return rc;
393}
394
[41987]395static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
[41970]396{
[41987]397 PTFTPSESSION pTftpSession = NULL;
[41970]398 int rc = VINF_SUCCESS;
399 int idxSession;
[41987]400 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
401 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
402 AssertPtrReturn(ppTftpSession, VERR_INVALID_PARAMETER);
[41970]403
404 for (idxSession = 0; idxSession < TFTP_SESSIONS_MAX; idxSession++)
405 {
[41987]406 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxSession];
[41970]407
408 if (!pTftpSession->fInUse)
[14964]409 goto found;
[1]410
[14964]411 /* sessions time out after 5 inactive seconds */
[41970]412 if ((int)(curtime - pTftpSession->iTimestamp) > 5000)
[14964]413 goto found;
414 }
[1]415
[41987]416 return VERR_NOT_FOUND;
[1]417
418 found:
[91631]419 if (pTftpSession->pcszFilenameHost != NULL)
420 {
421 RTStrFree((char *)pTftpSession->pcszFilenameHost);
422 // pTftpSession->pcszFilenameHost = NULL; /* will be zeroed out below */
423 }
424 RT_ZERO(*pTftpSession);
425
[41970]426 memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
427 pTftpSession->u16ClientPort = pcTftpIpHeader->UdpHdr.uh_sport;
428 rc = tftpSessionOptionParse(pTftpSession, pcTftpIpHeader);
[41987]429 AssertRCReturn(rc, VERR_INTERNAL_ERROR);
430 *ppTftpSession = pTftpSession;
[1]431
[91626]432 LogRel(("NAT: TFTP RRQ %s", pTftpSession->szFilename));
433 const char *pszPrefix = " ";
434 if (pTftpSession->OptionBlkSize.fRequested)
435 {
436 LogRel(("%s" "blksize=%RU64", pszPrefix, pTftpSession->OptionBlkSize.u64Value));
437 pszPrefix = ", ";
438 }
439 if (pTftpSession->OptionTSize.fRequested)
440 {
441 LogRel(("%s" "tsize=%RU64", pszPrefix, pTftpSession->OptionTSize.u64Value));
442 pszPrefix = ", ";
443 }
444 if (pTftpSession->OptionTimeout.fRequested)
445 {
446 LogRel(("%s" "timeout=%RU64", pszPrefix, pTftpSession->OptionTimeout.u64Value));
447 pszPrefix = ", ";
448 }
449 LogRel(("\n"));
450
[41970]451 tftpSessionUpdate(pData, pTftpSession);
[1]452
[41987]453 return VINF_SUCCESS;
[1]454}
455
[41987]456static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSessions)
[1]457{
[41970]458 PTFTPSESSION pTftpSession;
[41987]459 int idxTftpSession;
460 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
461 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
462 AssertPtrReturn(ppTftpSessions, VERR_INVALID_PARAMETER);
[1]463
[41987]464 for (idxTftpSession = 0; idxTftpSession < TFTP_SESSIONS_MAX; idxTftpSession++)
[14964]465 {
[41987]466 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxTftpSession];
[1]467
[41970]468 if (pTftpSession->fInUse)
[14964]469 {
[41970]470 if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
[14964]471 {
[41970]472 if (pTftpSession->u16ClientPort == pcTftpIpHeader->UdpHdr.uh_sport)
[41987]473 {
474 *ppTftpSessions = pTftpSession;
475 return VINF_SUCCESS;
476 }
[14964]477 }
[14470]478 }
[1]479 }
480
[41987]481 return VERR_NOT_FOUND;
[1]482}
483
[91631]484DECLINLINE(int) pftpSessionOpenFile(PTFTPSESSION pTftpSession, PRTFILE pSessionFile)
[1]485{
[64585]486 int rc;
[42180]487 LogFlowFuncEnter();
[1]488
[91631]489 if (pTftpSession->pcszFilenameHost == NULL)
[41970]490 {
[91631]491 rc = VERR_FILE_NOT_FOUND;
492 }
493 else
494 {
495 rc = RTFileOpen(pSessionFile, pTftpSession->pcszFilenameHost,
496 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
497 if (RT_FAILURE(rc))
[64585]498 rc = VERR_FILE_NOT_FOUND;
[41970]499 }
[91626]500
[41970]501 LogFlowFuncLeaveRC(rc);
502 return rc;
503}
[1016]504
[91631]505DECLINLINE(int) tftpSessionEvaluateOptions(PTFTPSESSION pTftpSession)
[41970]506{
[64585]507 int rc;
[41970]508 RTFILE hSessionFile;
509 uint64_t cbSessionFile = 0;
[76783]510 int cOptions;
[41970]511 LogFlowFunc(("pTftpSession:%p\n", pTftpSession));
512
[91631]513 rc = pftpSessionOpenFile(pTftpSession, &hSessionFile);
[41970]514 if (RT_FAILURE(rc))
[14964]515 {
[45652]516 LogFlowFuncLeaveRC(rc);
[41970]517 return rc;
[14964]518 }
[1]519
[80585]520 rc = RTFileQuerySize(hSessionFile, &cbSessionFile);
[41970]521 RTFileClose(hSessionFile);
522 if (RT_FAILURE(rc))
523 {
[45652]524 LogFlowFuncLeaveRC(rc);
[41970]525 return rc;
526 }
[1]527
[76783]528 cOptions = 0;
[76782]529
[41996]530 if (pTftpSession->OptionTSize.fRequested)
[42180]531 {
[45652]532 pTftpSession->OptionTSize.u64Value = cbSessionFile;
[76782]533 ++cOptions;
[42180]534 }
[76782]535
536 if (pTftpSession->OptionBlkSize.fRequested)
[41970]537 {
[76782]538 if (pTftpSession->OptionBlkSize.u64Value < 8)
539 {
540 /*
541 * we cannot make a counter-offer larger than the client's
542 * value, so just pretend we didn't recognize it and use
543 * default block size
544 */
545 pTftpSession->OptionBlkSize.fRequested = 0;
546 pTftpSession->OptionBlkSize.u64Value = 512;
547 }
548 else if (pTftpSession->OptionBlkSize.u64Value > 1428)
549 {
550 pTftpSession->OptionBlkSize.u64Value = 1428;
551 ++cOptions;
552 }
[41970]553 }
[76782]554 else
555 {
556 pTftpSession->OptionBlkSize.u64Value = 512;
557 }
558
559 rc = cOptions > 0 ? VINF_SUCCESS : VWRN_NOT_FOUND;
[41970]560 LogFlowFuncLeaveRC(rc);
561 return rc;
[1]562}
563
[41970]564DECLINLINE(int) tftpSend(PNATState pData,
565 PTFTPSESSION pTftpSession,
566 struct mbuf *pMBuf,
567 PCTFTPIPHDR pcTftpIpHeaderRecv)
[1016]568{
569 struct sockaddr_in saddr, daddr;
[80983]570 int error, rc;
571
[41970]572 LogFlowFunc(("pMBuf:%p, pcTftpIpHeaderRecv:%p\n", pMBuf, pcTftpIpHeaderRecv));
573 saddr.sin_addr = pcTftpIpHeaderRecv->IPv4Hdr.ip_dst;
574 saddr.sin_port = pcTftpIpHeaderRecv->UdpHdr.uh_dport;
575
576 daddr.sin_addr = pTftpSession->IpClientAddress;
577 daddr.sin_port = pTftpSession->u16ClientPort;
578
579
580 pMBuf->m_data += sizeof(struct udpiphdr);
581 pMBuf->m_len -= sizeof(struct udpiphdr);
[80983]582
583 error = udp_output2(pData, NULL, pMBuf, &saddr, &daddr, IPTOS_LOWDELAY);
584 rc = error ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
585
[41970]586 LogFlowFuncLeaveRC(rc);
587 return rc;
588}
589
[64585]590
591DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode,
592 const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv); /* gee wiz */
593
[41970]594DECLINLINE(int) tftpReadDataBlock(PNATState pData,
[41996]595 PTFTPSESSION pcTftpSession,
[41970]596 uint8_t *pu8Data,
597 int *pcbReadData)
598{
599 RTFILE hSessionFile;
600 int rc = VINF_SUCCESS;
[41996]601 uint16_t u16BlkSize = 0;
602 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
603 AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
604 AssertPtrReturn(pu8Data, VERR_INVALID_PARAMETER);
605 AssertPtrReturn(pcbReadData, VERR_INVALID_PARAMETER);
[42012]606 LogFlowFunc(("pcTftpSession:%p, pu8Data:%p, pcbReadData:%p\n",
[41996]607 pcTftpSession,
[41970]608 pu8Data,
609 pcbReadData));
610
[42180]611 u16BlkSize = (uint16_t)pcTftpSession->OptionBlkSize.u64Value;
[91631]612 rc = pftpSessionOpenFile(pcTftpSession, &hSessionFile);
[41970]613 if (RT_FAILURE(rc))
614 {
615 LogFlowFuncLeaveRC(rc);
616 return rc;
617 }
618
619 if (pcbReadData)
620 {
[63621]621 size_t cbRead;
622
[41970]623 rc = RTFileSeek(hSessionFile,
[42009]624 pcTftpSession->cbTransfered,
[41970]625 RTFILE_SEEK_BEGIN,
626 NULL);
627 if (RT_FAILURE(rc))
628 {
629 RTFileClose(hSessionFile);
630 LogFlowFuncLeaveRC(rc);
631 return rc;
632 }
[63621]633 rc = RTFileRead(hSessionFile, pu8Data, u16BlkSize, &cbRead);
[41970]634 if (RT_FAILURE(rc))
635 {
636 RTFileClose(hSessionFile);
637 LogFlowFuncLeaveRC(rc);
638 return rc;
639 }
[63621]640 *pcbReadData = (int)cbRead;
[41970]641 }
642
643 rc = RTFileClose(hSessionFile);
644
645 LogFlowFuncLeaveRC(rc);
646 return rc;
647}
648
[42180]649DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint64_t u64OptValue)
[41970]650{
[64585]651 char szOptionBuffer[256];
[63014]652 size_t iOptLength;
[41970]653 int rc = VINF_SUCCESS;
654 int cbMBufCurrent = pMBuf->m_len;
[42180]655 LogFlowFunc(("pMBuf:%p, pszOptName:%s, u16OptValue:%ld\n", pMBuf, pszOptName, u64OptValue));
[41970]656 AssertPtrReturn(pMBuf, VERR_INVALID_PARAMETER);
657 AssertPtrReturn(pszOptName, VERR_INVALID_PARAMETER);
658
[64585]659 RT_ZERO(szOptionBuffer);
660 iOptLength = RTStrPrintf(szOptionBuffer, 256 , "%s", pszOptName) + 1;
661 iOptLength += RTStrPrintf(szOptionBuffer + iOptLength, 256 - iOptLength , "%llu", u64OptValue) + 1;
[41970]662 if (iOptLength > M_TRAILINGSPACE(pMBuf))
663 rc = VERR_BUFFER_OVERFLOW; /* buffer too small */
664 else
665 {
[63014]666 pMBuf->m_len += (int)iOptLength;
[64585]667 m_copyback(pData, pMBuf, cbMBufCurrent, (int)iOptLength, szOptionBuffer);
[41970]668 }
669 LogFlowFuncLeaveRC(rc);
670 return rc;
671}
672
[80984]673
[41970]674DECLINLINE(int) tftpSendOACK(PNATState pData,
[64585]675 PTFTPSESSION pTftpSession,
676 PCTFTPIPHDR pcTftpIpHeaderRecv)
[41970]677{
[1016]678 struct mbuf *m;
[41970]679 PTFTPIPHDR pTftpIpHeader;
[64585]680 int rc;
[1016]681
[91631]682 rc = tftpSessionEvaluateOptions(pTftpSession);
[41970]683 if (RT_FAILURE(rc))
684 {
[91627]685 tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Option negotiation failure (file not found or inaccessible?)", pcTftpIpHeaderRecv);
[41970]686 LogFlowFuncLeave();
[80984]687 return rc;
[41970]688 }
689
[80984]690 if (rc == VWRN_NOT_FOUND)
691 return rc;
692
[36901]693 m = slirpTftpMbufAlloc(pData);
[80984]694 if (m == NULL)
695 {
696 tftpSessionTerminate(pTftpSession);
697 return VERR_NO_MEMORY;
698 }
[1016]699
700 m->m_data += if_maxlinkhdr;
[26404]701 m->m_pkthdr.header = mtod(m, void *);
[41970]702 pTftpIpHeader = mtod(m, PTFTPIPHDR);
703 m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */
[1033]704
[41970]705 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK);
[1016]706
[41996]707 if (pTftpSession->OptionBlkSize.fRequested)
[76782]708 rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->OptionBlkSize.u64Value);
709
[42190]710 if ( RT_SUCCESS(rc)
711 && pTftpSession->OptionTSize.fRequested)
[42180]712 rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->OptionTSize.u64Value);
[1033]713
[41970]714 rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
[80984]715 if (RT_FAILURE(rc))
716 tftpSessionTerminate(pTftpSession);
717
718 return rc;
[1016]719}
720
[80980]721
[41970]722DECLINLINE(int) tftpSendError(PNATState pData,
723 PTFTPSESSION pTftpSession,
724 uint16_t errorcode,
725 const char *msg,
726 PCTFTPIPHDR pcTftpIpHeaderRecv)
[1]727{
[41970]728 struct mbuf *m = NULL;
[1]729
[42180]730 LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg));
[36901]731 m = slirpTftpMbufAlloc(pData);
[80980]732 if (m != NULL)
[42180]733 {
[80980]734 u_int cbMsg = (u_int)strlen(msg) + 1; /* ending zero */
735 PTFTPIPHDR pTftpIpHeader;
[1]736
[80980]737 m->m_data += if_maxlinkhdr;
738 m->m_len = sizeof(TFTPIPHDR) + cbMsg;
739 m->m_pkthdr.header = mtod(m, void *);
740 pTftpIpHeader = mtod(m, PTFTPIPHDR);
[1]741
[80980]742 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
743 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);
[1033]744
[80980]745 m_copyback(pData, m, sizeof(TFTPIPHDR), cbMsg, (c_caddr_t)msg);
[1]746
[80980]747 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
748 }
[1]749
[41970]750 tftpSessionTerminate(pTftpSession);
[1]751
[42180]752 LogFlowFuncLeave();
[14964]753 return 0;
[1]754}
755
[80980]756
[41970]757static int tftpSendData(PNATState pData,
758 PTFTPSESSION pTftpSession,
[42020]759 uint16_t u16Block,
[41970]760 PCTFTPIPHDR pcTftpIpHeaderRecv)
[1]761{
[14964]762 struct mbuf *m;
[41970]763 PTFTPIPHDR pTftpIpHeader;
[42180]764 int cbRead = 0;
[41970]765 int rc = VINF_SUCCESS;
[1]766
[42020]767 if (u16Block == pTftpSession->cTftpAck)
768 pTftpSession->cTftpAck++;
769 else
770 {
[91627]771 tftpSendError(pData, pTftpSession, TFTP_EEXIST, "ACK is wrong", pcTftpIpHeaderRecv);
[42020]772 return -1;
773 }
774
[36901]775 m = slirpTftpMbufAlloc(pData);
[14964]776 if (!m)
777 return -1;
[1]778
[14964]779 m->m_data += if_maxlinkhdr;
[26404]780 m->m_pkthdr.header = mtod(m, void *);
[41970]781 pTftpIpHeader = mtod(m, PTFTPIPHDR);
782 m->m_len = sizeof(TFTPIPHDR);
[1]783
[41970]784 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
[42020]785 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck);
[1033]786
[76782]787 if (RT_LIKELY(M_TRAILINGSPACE(m) >= pTftpSession->OptionBlkSize.u64Value))
788 {
789 uint8_t *pu8Data = (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t);
790 rc = tftpReadDataBlock(pData, pTftpSession, pu8Data, &cbRead);
791 }
792 else
793 rc = VERR_BUFFER_OVERFLOW;
[1]794
[41970]795 if (RT_SUCCESS(rc))
[14964]796 {
[42009]797 pTftpSession->cbTransfered += cbRead;
798 m->m_len += cbRead;
[41970]799 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
[42009]800 if (cbRead > 0)
[41970]801 tftpSessionUpdate(pData, pTftpSession);
802 else
803 tftpSessionTerminate(pTftpSession);
804 }
805 else
806 {
[28443]807 m_freem(pData, m);
[91627]808 tftpSendError(pData, pTftpSession, TFTP_ENOENT, "File not found", pcTftpIpHeaderRecv);
[14964]809 /* send "file not found" error back */
810 return -1;
811 }
[1]812
[14964]813 return 0;
[1]814}
815
[41970]816DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen)
[1]817{
[41987]818 PTFTPSESSION pTftpSession = NULL;
[41970]819 uint8_t *pu8Payload = NULL;
820 int cbPayload = 0;
[42013]821 size_t cbFileName = 0;
[41987]822 int rc = VINF_SUCCESS;
[1]823
[41970]824 AssertPtrReturnVoid(pTftpIpHeader);
825 AssertPtrReturnVoid(pData);
826 AssertReturnVoid(pktlen > sizeof(TFTPIPHDR));
827 LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen));
[1]828
[41987]829 rc = tftpAllocateSession(pData, pTftpIpHeader, &pTftpSession);
830 if ( RT_FAILURE(rc)
831 || pTftpSession == NULL)
[14964]832 {
[41970]833 LogFlowFuncLeave();
834 return;
[1]835 }
[1033]836
[41970]837 pu8Payload = (uint8_t *)&pTftpIpHeader->Core;
838 cbPayload = pktlen - sizeof(TFTPIPHDR);
[1033]839
[41970]840 cbFileName = RTStrNLen((char *)pu8Payload, cbPayload);
841 /* We assume that file name should finish with '\0' and shouldn't bigger
842 * than buffer for name storage.
843 */
[66828]844 AssertReturnVoid( (ssize_t)cbFileName < cbPayload
[41970]845 && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */
846 && cbFileName);
[1033]847
[41970]848 /* Dont't bother with rest processing in case of invalid access */
849 if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession)))
[14964]850 {
[91627]851 tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Access violation", pTftpIpHeader);
[41970]852 LogFlowFuncLeave();
[14964]853 return;
854 }
[1033]855
[1]856
[1016]857
[41970]858 if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession)))
[14964]859 {
[91627]860 tftpSendError(pData, pTftpSession, TFTP_ENOSYS, "Unsupported transfer mode", pTftpIpHeader);
[41970]861 LogFlowFuncLeave();
[14964]862 return;
863 }
[1]864
865
[80984]866 rc = tftpSendOACK(pData, pTftpSession, pTftpIpHeader);
867 if (rc == VWRN_NOT_FOUND)
868 rc = tftpSendData(pData, pTftpSession, 0, pTftpIpHeader);
869
[41970]870 LogFlowFuncLeave();
871 return;
[1]872}
873
[41970]874static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader)
[1]875{
[41987]876 int rc;
877 PTFTPSESSION pTftpSession = NULL;
[1]878
[41987]879 rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
880 if (RT_FAILURE(rc))
[14964]881 return;
[1]882
[45652]883 if (tftpSendData(pData, pTftpSession,
884 RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode),
885 pTftpIpHeader))
[91626]886 LogRel(("NAT: TFTP send failed\n"));
[1]887}
888
[43233]889int slirpTftpInit(PNATState pData)
[1]890{
[41987]891 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
892 pData->pvTftpSessions = RTMemAllocZ(sizeof(TFTPSESSION) * TFTP_SESSIONS_MAX);
893 AssertPtrReturn(pData->pvTftpSessions, VERR_NO_MEMORY);
894 return VINF_SUCCESS;
895}
896
[43233]897void slirpTftpTerm(PNATState pData)
[41987]898{
[43233]899 RTMemFree(pData->pvTftpSessions);
900}
901
902int slirpTftpInput(PNATState pData, struct mbuf *pMbuf)
903{
[41970]904 PTFTPIPHDR pTftpIpHeader = NULL;
905 AssertPtr(pData);
906 AssertPtr(pMbuf);
907 pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR);
[1]908
[41970]909 switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType))
[14964]910 {
911 case TFTP_RRQ:
[41970]912 tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL));
[14964]913 break;
[1]914
[14964]915 case TFTP_ACK:
[41970]916 tftpProcessACK(pData, pTftpIpHeader);
[14964]917 break;
[80986]918
919 case TFTP_ERROR:
920 {
921 PTFTPSESSION pTftpSession;
922 int rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
923 if (RT_SUCCESS(rc))
924 tftpSessionTerminate(pTftpSession);
925 }
926
[41987]927 default:;
[14964]928 }
[41987]929 LogFlowFuncLeaveRC(VINF_SUCCESS);
930 return VINF_SUCCESS;
[1]931}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use