VirtualBox

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

Last change on this file since 93228 was 93228, checked in by vboxsync, 2 years ago

NAT/tftp: bugref:9350 - Fix the ".." check.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use