VirtualBox

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

Last change on this file since 91942 was 91631, checked in by vboxsync, 3 years ago

NAT/tftp: bugref:9350 - Redo r128102. Just reject pathnames that try
to use "..", like BSD tftpd does. Always convert backslashes into
forward slashes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
Line 
1/* $Id: tftp.c 91631 2021-10-07 22:00:09Z vboxsync $ */
2/** @file
3 * NAT - TFTP server.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 attempts to break out of tftp dir */
159 if (RTStrStartsWith(pTftpSession->szFilename, "../"))
160 goto done;
161
162 const char *dotdot = RTStrStr(pTftpSession->szFilename, "/..");
163 if (dotdot != NULL && (dotdot[3] == '/' || dotdot[3] == '\0'))
164 goto done;
165
166 char *pszPathHostAbs;
167 int cbLen = RTStrAPrintf(&pszPathHostAbs, "%s/%s",
168 tftp_prefix, pTftpSession->szFilename);
169 if (cbLen == -1)
170 goto done;
171
172 LogRel2(("NAT: TFTP: %s\n", pszPathHostAbs));
173 pTftpSession->pcszFilenameHost = pszPathHostAbs;
174 rc = VINF_SUCCESS;
175
176 done:
177 LogFlowFuncLeaveRC(rc);
178 return rc;
179}
180
181/*
182 * This function returns index of option descriptor in passed descriptor array
183 * @param piIdxOpt returned index value
184 * @param paTftpDesc array of known Tftp descriptors
185 * @param caTftpDesc size of array of tftp descriptors
186 * @param pszOpt name of option
187 */
188DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
189{
190 int rc = VINF_SUCCESS;
191 int idxOption = 0;
192 AssertReturn(piIdxOpt, VERR_INVALID_PARAMETER);
193 AssertReturn(paTftpDesc, VERR_INVALID_PARAMETER);
194 AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
195 for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
196 {
197 if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
198 {
199 *piIdxOpt = idxOption;
200 return rc;
201 }
202 }
203 rc = VERR_NOT_FOUND;
204 return rc;
205}
206
207/**
208 * Helper function to look for index of descriptor in transfer format descriptors
209 * @param piIdxOpt returned value of index
210 * @param pszOpt name of option
211 */
212DECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
213{
214 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
215}
216
217/**
218 * Helper function to look for index of descriptor in options descriptors
219 * @param piIdxOpt returned value of index
220 * @param pszOpt name of option
221 */
222DECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
223{
224 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
225}
226
227
228#if 0 /* unused */
229DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
230{
231 int idxOptDesc = 0;
232 AssertPtrReturn(pszOptionName, false);
233 AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
234 AssertReturn(RTStrNLen(pszOptionName,10) < 8, false);
235 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
236 {
237 if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
238 return true;
239 }
240 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
241 {
242 if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
243 return true;
244 }
245 return false;
246}
247#endif /* unused */
248
249
250/**
251 * This helper function that validate if client want to operate in supported by server mode.
252 * @param pcTftpHeader comulative header (IP, UDP, TFTP)
253 * @param pcu8Options pointer to the options supposing that pointer points at the mode option
254 * @param cbOptions size of the options buffer
255 */
256DECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
257{
258 AssertPtrReturn(pcTftpSession, 0);
259 return (pcTftpSession->enmTftpFmt == TFTPFMT_OCTET);
260}
261
262
263DECLINLINE(void) tftpSessionUpdate(PNATState pData, PTFTPSESSION pTftpSession)
264{
265 pTftpSession->iTimestamp = curtime;
266 pTftpSession->fInUse = 1;
267}
268
269DECLINLINE(void) tftpSessionTerminate(PTFTPSESSION pTftpSession)
270{
271 if (pTftpSession->pcszFilenameHost != NULL)
272 {
273 RTStrFree((char *)pTftpSession->pcszFilenameHost);
274 pTftpSession->pcszFilenameHost = NULL;
275 }
276
277 pTftpSession->fInUse = 0;
278}
279
280DECLINLINE(int) tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
281{
282 int rc = VINF_SUCCESS;
283 rc = RTStrToInt64Full(pcszRawOption, 0, (int64_t *)&pTftpSessionOption->u64Value);
284 AssertRCReturn(rc, rc);
285 pTftpSessionOption->fRequested = 1;
286 return rc;
287}
288
289DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
290{
291 int rc = VINF_SUCCESS;
292 char *pszTftpRRQRaw;
293 size_t idxTftpRRQRaw = 0;
294 ssize_t cbTftpRRQRaw = 0;
295 int fWithArg = 0;
296 int idxOptionArg = 0;
297
298 AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
299 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
300 AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
301 LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
302
303 pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
304 cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_UOFFSETOF(TFTPIPHDR, Core);
305 while (cbTftpRRQRaw)
306 {
307 rc = RTStrNLenEx(pszTftpRRQRaw, cbTftpRRQRaw, &idxTftpRRQRaw);
308 if (RT_SUCCESS(rc))
309 ++idxTftpRRQRaw; /* count the NUL too */
310 else
311 break;
312
313 if (RTStrNLen(pTftpSession->szFilename, TFTP_FILENAME_MAX) == 0)
314 {
315 rc = RTStrCopy(pTftpSession->szFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
316 if (RT_FAILURE(rc))
317 {
318 LogFlowFuncLeaveRC(rc);
319 AssertRCReturn(rc,rc);
320 }
321 }
322 else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
323 {
324 int idxFmt = 0;
325 rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
326 if (RT_FAILURE(rc))
327 {
328 LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
329 return VERR_INTERNAL_ERROR;
330 }
331 AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
332 && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
333 pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
334 }
335 else if (fWithArg)
336 {
337 if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
338 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
339
340 if ( RT_SUCCESS(rc)
341 && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
342 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize);
343
344 /** @todo we don't use timeout, but its value in the range 0-255 */
345 if ( RT_SUCCESS(rc)
346 && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName))
347 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout);
348
349 /** @todo unknown option detection */
350 if (RT_FAILURE(rc))
351 {
352 LogFlowFuncLeaveRC(rc);
353 AssertRCReturn(rc,rc);
354 }
355 fWithArg = 0;
356 idxOptionArg = 0;
357 }
358 else
359 {
360 rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
361 if (RT_SUCCESS(rc))
362 fWithArg = 1;
363 else
364 {
365 LogFlowFuncLeaveRC(rc);
366 AssertRCReturn(rc,rc);
367 }
368 }
369 pszTftpRRQRaw += idxTftpRRQRaw;
370 cbTftpRRQRaw -= idxTftpRRQRaw;
371 }
372
373 LogFlowFuncLeaveRC(rc);
374 return rc;
375}
376
377static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
378{
379 PTFTPSESSION pTftpSession = NULL;
380 int rc = VINF_SUCCESS;
381 int idxSession;
382 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
383 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
384 AssertPtrReturn(ppTftpSession, VERR_INVALID_PARAMETER);
385
386 for (idxSession = 0; idxSession < TFTP_SESSIONS_MAX; idxSession++)
387 {
388 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxSession];
389
390 if (!pTftpSession->fInUse)
391 goto found;
392
393 /* sessions time out after 5 inactive seconds */
394 if ((int)(curtime - pTftpSession->iTimestamp) > 5000)
395 goto found;
396 }
397
398 return VERR_NOT_FOUND;
399
400 found:
401 if (pTftpSession->pcszFilenameHost != NULL)
402 {
403 RTStrFree((char *)pTftpSession->pcszFilenameHost);
404 // pTftpSession->pcszFilenameHost = NULL; /* will be zeroed out below */
405 }
406 RT_ZERO(*pTftpSession);
407
408 memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
409 pTftpSession->u16ClientPort = pcTftpIpHeader->UdpHdr.uh_sport;
410 rc = tftpSessionOptionParse(pTftpSession, pcTftpIpHeader);
411 AssertRCReturn(rc, VERR_INTERNAL_ERROR);
412 *ppTftpSession = pTftpSession;
413
414 LogRel(("NAT: TFTP RRQ %s", pTftpSession->szFilename));
415 const char *pszPrefix = " ";
416 if (pTftpSession->OptionBlkSize.fRequested)
417 {
418 LogRel(("%s" "blksize=%RU64", pszPrefix, pTftpSession->OptionBlkSize.u64Value));
419 pszPrefix = ", ";
420 }
421 if (pTftpSession->OptionTSize.fRequested)
422 {
423 LogRel(("%s" "tsize=%RU64", pszPrefix, pTftpSession->OptionTSize.u64Value));
424 pszPrefix = ", ";
425 }
426 if (pTftpSession->OptionTimeout.fRequested)
427 {
428 LogRel(("%s" "timeout=%RU64", pszPrefix, pTftpSession->OptionTimeout.u64Value));
429 pszPrefix = ", ";
430 }
431 LogRel(("\n"));
432
433 tftpSessionUpdate(pData, pTftpSession);
434
435 return VINF_SUCCESS;
436}
437
438static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSessions)
439{
440 PTFTPSESSION pTftpSession;
441 int idxTftpSession;
442 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
443 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
444 AssertPtrReturn(ppTftpSessions, VERR_INVALID_PARAMETER);
445
446 for (idxTftpSession = 0; idxTftpSession < TFTP_SESSIONS_MAX; idxTftpSession++)
447 {
448 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxTftpSession];
449
450 if (pTftpSession->fInUse)
451 {
452 if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
453 {
454 if (pTftpSession->u16ClientPort == pcTftpIpHeader->UdpHdr.uh_sport)
455 {
456 *ppTftpSessions = pTftpSession;
457 return VINF_SUCCESS;
458 }
459 }
460 }
461 }
462
463 return VERR_NOT_FOUND;
464}
465
466DECLINLINE(int) pftpSessionOpenFile(PTFTPSESSION pTftpSession, PRTFILE pSessionFile)
467{
468 int rc;
469 LogFlowFuncEnter();
470
471 if (pTftpSession->pcszFilenameHost == NULL)
472 {
473 rc = VERR_FILE_NOT_FOUND;
474 }
475 else
476 {
477 rc = RTFileOpen(pSessionFile, pTftpSession->pcszFilenameHost,
478 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
479 if (RT_FAILURE(rc))
480 rc = VERR_FILE_NOT_FOUND;
481 }
482
483 LogFlowFuncLeaveRC(rc);
484 return rc;
485}
486
487DECLINLINE(int) tftpSessionEvaluateOptions(PTFTPSESSION pTftpSession)
488{
489 int rc;
490 RTFILE hSessionFile;
491 uint64_t cbSessionFile = 0;
492 int cOptions;
493 LogFlowFunc(("pTftpSession:%p\n", pTftpSession));
494
495 rc = pftpSessionOpenFile(pTftpSession, &hSessionFile);
496 if (RT_FAILURE(rc))
497 {
498 LogFlowFuncLeaveRC(rc);
499 return rc;
500 }
501
502 rc = RTFileQuerySize(hSessionFile, &cbSessionFile);
503 RTFileClose(hSessionFile);
504 if (RT_FAILURE(rc))
505 {
506 LogFlowFuncLeaveRC(rc);
507 return rc;
508 }
509
510 cOptions = 0;
511
512 if (pTftpSession->OptionTSize.fRequested)
513 {
514 pTftpSession->OptionTSize.u64Value = cbSessionFile;
515 ++cOptions;
516 }
517
518 if (pTftpSession->OptionBlkSize.fRequested)
519 {
520 if (pTftpSession->OptionBlkSize.u64Value < 8)
521 {
522 /*
523 * we cannot make a counter-offer larger than the client's
524 * value, so just pretend we didn't recognize it and use
525 * default block size
526 */
527 pTftpSession->OptionBlkSize.fRequested = 0;
528 pTftpSession->OptionBlkSize.u64Value = 512;
529 }
530 else if (pTftpSession->OptionBlkSize.u64Value > 1428)
531 {
532 pTftpSession->OptionBlkSize.u64Value = 1428;
533 ++cOptions;
534 }
535 }
536 else
537 {
538 pTftpSession->OptionBlkSize.u64Value = 512;
539 }
540
541 rc = cOptions > 0 ? VINF_SUCCESS : VWRN_NOT_FOUND;
542 LogFlowFuncLeaveRC(rc);
543 return rc;
544}
545
546DECLINLINE(int) tftpSend(PNATState pData,
547 PTFTPSESSION pTftpSession,
548 struct mbuf *pMBuf,
549 PCTFTPIPHDR pcTftpIpHeaderRecv)
550{
551 struct sockaddr_in saddr, daddr;
552 int error, rc;
553
554 LogFlowFunc(("pMBuf:%p, pcTftpIpHeaderRecv:%p\n", pMBuf, pcTftpIpHeaderRecv));
555 saddr.sin_addr = pcTftpIpHeaderRecv->IPv4Hdr.ip_dst;
556 saddr.sin_port = pcTftpIpHeaderRecv->UdpHdr.uh_dport;
557
558 daddr.sin_addr = pTftpSession->IpClientAddress;
559 daddr.sin_port = pTftpSession->u16ClientPort;
560
561
562 pMBuf->m_data += sizeof(struct udpiphdr);
563 pMBuf->m_len -= sizeof(struct udpiphdr);
564
565 error = udp_output2(pData, NULL, pMBuf, &saddr, &daddr, IPTOS_LOWDELAY);
566 rc = error ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
567
568 LogFlowFuncLeaveRC(rc);
569 return rc;
570}
571
572
573DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode,
574 const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv); /* gee wiz */
575
576DECLINLINE(int) tftpReadDataBlock(PNATState pData,
577 PTFTPSESSION pcTftpSession,
578 uint8_t *pu8Data,
579 int *pcbReadData)
580{
581 RTFILE hSessionFile;
582 int rc = VINF_SUCCESS;
583 uint16_t u16BlkSize = 0;
584 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
585 AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
586 AssertPtrReturn(pu8Data, VERR_INVALID_PARAMETER);
587 AssertPtrReturn(pcbReadData, VERR_INVALID_PARAMETER);
588 LogFlowFunc(("pcTftpSession:%p, pu8Data:%p, pcbReadData:%p\n",
589 pcTftpSession,
590 pu8Data,
591 pcbReadData));
592
593 u16BlkSize = (uint16_t)pcTftpSession->OptionBlkSize.u64Value;
594 rc = pftpSessionOpenFile(pcTftpSession, &hSessionFile);
595 if (RT_FAILURE(rc))
596 {
597 LogFlowFuncLeaveRC(rc);
598 return rc;
599 }
600
601 if (pcbReadData)
602 {
603 size_t cbRead;
604
605 rc = RTFileSeek(hSessionFile,
606 pcTftpSession->cbTransfered,
607 RTFILE_SEEK_BEGIN,
608 NULL);
609 if (RT_FAILURE(rc))
610 {
611 RTFileClose(hSessionFile);
612 LogFlowFuncLeaveRC(rc);
613 return rc;
614 }
615 rc = RTFileRead(hSessionFile, pu8Data, u16BlkSize, &cbRead);
616 if (RT_FAILURE(rc))
617 {
618 RTFileClose(hSessionFile);
619 LogFlowFuncLeaveRC(rc);
620 return rc;
621 }
622 *pcbReadData = (int)cbRead;
623 }
624
625 rc = RTFileClose(hSessionFile);
626
627 LogFlowFuncLeaveRC(rc);
628 return rc;
629}
630
631DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint64_t u64OptValue)
632{
633 char szOptionBuffer[256];
634 size_t iOptLength;
635 int rc = VINF_SUCCESS;
636 int cbMBufCurrent = pMBuf->m_len;
637 LogFlowFunc(("pMBuf:%p, pszOptName:%s, u16OptValue:%ld\n", pMBuf, pszOptName, u64OptValue));
638 AssertPtrReturn(pMBuf, VERR_INVALID_PARAMETER);
639 AssertPtrReturn(pszOptName, VERR_INVALID_PARAMETER);
640
641 RT_ZERO(szOptionBuffer);
642 iOptLength = RTStrPrintf(szOptionBuffer, 256 , "%s", pszOptName) + 1;
643 iOptLength += RTStrPrintf(szOptionBuffer + iOptLength, 256 - iOptLength , "%llu", u64OptValue) + 1;
644 if (iOptLength > M_TRAILINGSPACE(pMBuf))
645 rc = VERR_BUFFER_OVERFLOW; /* buffer too small */
646 else
647 {
648 pMBuf->m_len += (int)iOptLength;
649 m_copyback(pData, pMBuf, cbMBufCurrent, (int)iOptLength, szOptionBuffer);
650 }
651 LogFlowFuncLeaveRC(rc);
652 return rc;
653}
654
655
656DECLINLINE(int) tftpSendOACK(PNATState pData,
657 PTFTPSESSION pTftpSession,
658 PCTFTPIPHDR pcTftpIpHeaderRecv)
659{
660 struct mbuf *m;
661 PTFTPIPHDR pTftpIpHeader;
662 int rc;
663
664 rc = tftpSessionEvaluateOptions(pTftpSession);
665 if (RT_FAILURE(rc))
666 {
667 tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Option negotiation failure (file not found or inaccessible?)", pcTftpIpHeaderRecv);
668 LogFlowFuncLeave();
669 return rc;
670 }
671
672 if (rc == VWRN_NOT_FOUND)
673 return rc;
674
675 m = slirpTftpMbufAlloc(pData);
676 if (m == NULL)
677 {
678 tftpSessionTerminate(pTftpSession);
679 return VERR_NO_MEMORY;
680 }
681
682 m->m_data += if_maxlinkhdr;
683 m->m_pkthdr.header = mtod(m, void *);
684 pTftpIpHeader = mtod(m, PTFTPIPHDR);
685 m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */
686
687 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK);
688
689 if (pTftpSession->OptionBlkSize.fRequested)
690 rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->OptionBlkSize.u64Value);
691
692 if ( RT_SUCCESS(rc)
693 && pTftpSession->OptionTSize.fRequested)
694 rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->OptionTSize.u64Value);
695
696 rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
697 if (RT_FAILURE(rc))
698 tftpSessionTerminate(pTftpSession);
699
700 return rc;
701}
702
703
704DECLINLINE(int) tftpSendError(PNATState pData,
705 PTFTPSESSION pTftpSession,
706 uint16_t errorcode,
707 const char *msg,
708 PCTFTPIPHDR pcTftpIpHeaderRecv)
709{
710 struct mbuf *m = NULL;
711
712 LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg));
713 m = slirpTftpMbufAlloc(pData);
714 if (m != NULL)
715 {
716 u_int cbMsg = (u_int)strlen(msg) + 1; /* ending zero */
717 PTFTPIPHDR pTftpIpHeader;
718
719 m->m_data += if_maxlinkhdr;
720 m->m_len = sizeof(TFTPIPHDR) + cbMsg;
721 m->m_pkthdr.header = mtod(m, void *);
722 pTftpIpHeader = mtod(m, PTFTPIPHDR);
723
724 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
725 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);
726
727 m_copyback(pData, m, sizeof(TFTPIPHDR), cbMsg, (c_caddr_t)msg);
728
729 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
730 }
731
732 tftpSessionTerminate(pTftpSession);
733
734 LogFlowFuncLeave();
735 return 0;
736}
737
738
739static int tftpSendData(PNATState pData,
740 PTFTPSESSION pTftpSession,
741 uint16_t u16Block,
742 PCTFTPIPHDR pcTftpIpHeaderRecv)
743{
744 struct mbuf *m;
745 PTFTPIPHDR pTftpIpHeader;
746 int cbRead = 0;
747 int rc = VINF_SUCCESS;
748
749 if (u16Block == pTftpSession->cTftpAck)
750 pTftpSession->cTftpAck++;
751 else
752 {
753 tftpSendError(pData, pTftpSession, TFTP_EEXIST, "ACK is wrong", pcTftpIpHeaderRecv);
754 return -1;
755 }
756
757 m = slirpTftpMbufAlloc(pData);
758 if (!m)
759 return -1;
760
761 m->m_data += if_maxlinkhdr;
762 m->m_pkthdr.header = mtod(m, void *);
763 pTftpIpHeader = mtod(m, PTFTPIPHDR);
764 m->m_len = sizeof(TFTPIPHDR);
765
766 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
767 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck);
768
769 if (RT_LIKELY(M_TRAILINGSPACE(m) >= pTftpSession->OptionBlkSize.u64Value))
770 {
771 uint8_t *pu8Data = (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t);
772 rc = tftpReadDataBlock(pData, pTftpSession, pu8Data, &cbRead);
773 }
774 else
775 rc = VERR_BUFFER_OVERFLOW;
776
777 if (RT_SUCCESS(rc))
778 {
779 pTftpSession->cbTransfered += cbRead;
780 m->m_len += cbRead;
781 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
782 if (cbRead > 0)
783 tftpSessionUpdate(pData, pTftpSession);
784 else
785 tftpSessionTerminate(pTftpSession);
786 }
787 else
788 {
789 m_freem(pData, m);
790 tftpSendError(pData, pTftpSession, TFTP_ENOENT, "File not found", pcTftpIpHeaderRecv);
791 /* send "file not found" error back */
792 return -1;
793 }
794
795 return 0;
796}
797
798DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen)
799{
800 PTFTPSESSION pTftpSession = NULL;
801 uint8_t *pu8Payload = NULL;
802 int cbPayload = 0;
803 size_t cbFileName = 0;
804 int rc = VINF_SUCCESS;
805
806 AssertPtrReturnVoid(pTftpIpHeader);
807 AssertPtrReturnVoid(pData);
808 AssertReturnVoid(pktlen > sizeof(TFTPIPHDR));
809 LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen));
810
811 rc = tftpAllocateSession(pData, pTftpIpHeader, &pTftpSession);
812 if ( RT_FAILURE(rc)
813 || pTftpSession == NULL)
814 {
815 LogFlowFuncLeave();
816 return;
817 }
818
819 pu8Payload = (uint8_t *)&pTftpIpHeader->Core;
820 cbPayload = pktlen - sizeof(TFTPIPHDR);
821
822 cbFileName = RTStrNLen((char *)pu8Payload, cbPayload);
823 /* We assume that file name should finish with '\0' and shouldn't bigger
824 * than buffer for name storage.
825 */
826 AssertReturnVoid( (ssize_t)cbFileName < cbPayload
827 && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */
828 && cbFileName);
829
830 /* Dont't bother with rest processing in case of invalid access */
831 if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession)))
832 {
833 tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Access violation", pTftpIpHeader);
834 LogFlowFuncLeave();
835 return;
836 }
837
838
839
840 if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession)))
841 {
842 tftpSendError(pData, pTftpSession, TFTP_ENOSYS, "Unsupported transfer mode", pTftpIpHeader);
843 LogFlowFuncLeave();
844 return;
845 }
846
847
848 rc = tftpSendOACK(pData, pTftpSession, pTftpIpHeader);
849 if (rc == VWRN_NOT_FOUND)
850 rc = tftpSendData(pData, pTftpSession, 0, pTftpIpHeader);
851
852 LogFlowFuncLeave();
853 return;
854}
855
856static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader)
857{
858 int rc;
859 PTFTPSESSION pTftpSession = NULL;
860
861 rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
862 if (RT_FAILURE(rc))
863 return;
864
865 if (tftpSendData(pData, pTftpSession,
866 RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode),
867 pTftpIpHeader))
868 LogRel(("NAT: TFTP send failed\n"));
869}
870
871int slirpTftpInit(PNATState pData)
872{
873 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
874 pData->pvTftpSessions = RTMemAllocZ(sizeof(TFTPSESSION) * TFTP_SESSIONS_MAX);
875 AssertPtrReturn(pData->pvTftpSessions, VERR_NO_MEMORY);
876 return VINF_SUCCESS;
877}
878
879void slirpTftpTerm(PNATState pData)
880{
881 RTMemFree(pData->pvTftpSessions);
882}
883
884int slirpTftpInput(PNATState pData, struct mbuf *pMbuf)
885{
886 PTFTPIPHDR pTftpIpHeader = NULL;
887 AssertPtr(pData);
888 AssertPtr(pMbuf);
889 pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR);
890
891 switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType))
892 {
893 case TFTP_RRQ:
894 tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL));
895 break;
896
897 case TFTP_ACK:
898 tftpProcessACK(pData, pTftpIpHeader);
899 break;
900
901 case TFTP_ERROR:
902 {
903 PTFTPSESSION pTftpSession;
904 int rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
905 if (RT_SUCCESS(rc))
906 tftpSessionTerminate(pTftpSession);
907 }
908
909 default:;
910 }
911 LogFlowFuncLeaveRC(VINF_SUCCESS);
912 return VINF_SUCCESS;
913}
Note: See TracBrowser for help on using the repository browser.

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