VirtualBox

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

Last change on this file since 77750 was 76783, checked in by vboxsync, 6 years ago

NAT: fix previous, MS doesn't do C99.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.8 KB
Line 
1/* $Id: tftp.c 76783 2019-01-11 16:47:35Z vboxsync $ */
2/** @file
3 * NAT - TFTP server.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 <[email protected]>
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/asm-math.h>
46#include <iprt/file.h>
47#include <iprt/err.h>
48#include <iprt/path.h>
49
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 TFPTPSESSIONOPTDESC
60{
61 int fRequested;
62 uint64_t u64Value;
63} TFPTPSESSIONOPTDESC, *PTFPTPSESSIONOPTDESC;
64
65typedef struct TFTPSESSION
66{
67 int fInUse;
68 unsigned char pszFilename[TFTP_FILENAME_MAX];
69 struct in_addr IpClientAddress;
70 uint16_t u16ClientPort;
71 int iTimestamp;
72 uint64_t cbTransfered;
73 uint16_t cTftpAck;
74 ENMTFTPSESSIONFMT enmTftpFmt;
75 TFPTPSESSIONOPTDESC OptionBlkSize;
76 TFPTPSESSIONOPTDESC OptionTSize;
77 TFPTPSESSIONOPTDESC OptionTimeout;
78} TFTPSESSION, *PTFTPSESSION, **PPTFTPSESSION;
79
80#pragma pack(1)
81typedef struct TFTPCOREHDR
82{
83 uint16_t u16TftpOpCode;
84 /* Data lays here (might be raw uint8_t* or header of payload ) */
85} TFTPCOREHDR, *PTFTPCOREHDR;
86
87typedef struct TFTPIPHDR
88{
89 struct ip IPv4Hdr;
90 struct udphdr UdpHdr;
91 uint16_t u16TftpOpType;
92 TFTPCOREHDR Core;
93 /* Data lays here */
94} TFTPIPHDR, *PTFTPIPHDR;
95#pragma pack()
96
97typedef const PTFTPIPHDR PCTFTPIPHDR;
98
99typedef const PTFTPSESSION PCTFTPSESSION;
100
101
102typedef struct TFTPOPTIONDESC
103{
104 const char *pszName;
105 ENMTFTPSESSIONFMT enmType;
106 int cbName;
107 bool fHasValue;
108} TFTPOPTIONDESC, *PTFTPOPTIONDESC;
109
110typedef const PTFTPOPTIONDESC PCTFTPOPTIONDESC;
111static TFTPOPTIONDESC g_TftpTransferFmtDesc[] =
112{
113 {"octet", TFTPFMT_OCTET, 5, false}, /* RFC1350 */
114 {"netascii", TFTPFMT_NETASCII, 8, false}, /* RFC1350 */
115 {"mail", TFTPFMT_MAIL, 4, false}, /* RFC1350 */
116};
117
118static TFTPOPTIONDESC g_TftpDesc[] =
119{
120 {"blksize", TFTPFMT_NOT_FMT, 7, true}, /* RFC2348 */
121 {"timeout", TFTPFMT_NOT_FMT, 7, true}, /* RFC2349 */
122 {"tsize", TFTPFMT_NOT_FMT, 5, true}, /* RFC2349 */
123 {"size", TFTPFMT_NOT_FMT, 4, true}, /* RFC2349 */
124};
125
126/**
127 * This function evaluate file name.
128 * @param pu8Payload
129 * @param cbPayload
130 * @param cbFileName
131 * @return VINF_SUCCESS -
132 * VERR_INVALID_PARAMETER -
133 */
134DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PCTFTPSESSION pcTftpSession)
135{
136 int rc = VINF_SUCCESS;
137 AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
138
139 /* only allow exported prefixes */
140 if (!tftp_prefix)
141 rc = VERR_INTERNAL_ERROR;
142 else
143 {
144 char *pszFullPathAbs = RTPathAbsExDup(tftp_prefix, (const char*)pcTftpSession->pszFilename);
145
146 if ( !pszFullPathAbs
147 || !RTPathStartsWith(pszFullPathAbs, tftp_prefix))
148 rc = VERR_FILE_NOT_FOUND;
149
150 RTStrFree(pszFullPathAbs);
151 }
152 LogFlowFuncLeaveRC(rc);
153 return rc;
154}
155
156/*
157 * This function returns index of option descriptor in passed descriptor array
158 * @param piIdxOpt returned index value
159 * @param paTftpDesc array of known Tftp descriptors
160 * @param caTftpDesc size of array of tftp descriptors
161 * @param pszOpt name of option
162 */
163DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
164{
165 int rc = VINF_SUCCESS;
166 int idxOption = 0;
167 AssertReturn(piIdxOpt, VERR_INVALID_PARAMETER);
168 AssertReturn(paTftpDesc, VERR_INVALID_PARAMETER);
169 AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
170 for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
171 {
172 if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
173 {
174 *piIdxOpt = idxOption;
175 return rc;
176 }
177 }
178 rc = VERR_NOT_FOUND;
179 return rc;
180}
181
182/**
183 * Helper function to look for index of descriptor in transfer format descriptors
184 * @param piIdxOpt returned value of index
185 * @param pszOpt name of option
186 */
187DECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
188{
189 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
190}
191
192/**
193 * Helper function to look for index of descriptor in options descriptors
194 * @param piIdxOpt returned value of index
195 * @param pszOpt name of option
196 */
197DECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
198{
199 return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
200}
201
202
203#if 0 /* unused */
204DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
205{
206 int idxOptDesc = 0;
207 AssertPtrReturn(pszOptionName, false);
208 AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
209 AssertReturn(RTStrNLen(pszOptionName,10) < 8, false);
210 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
211 {
212 if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
213 return true;
214 }
215 for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
216 {
217 if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
218 return true;
219 }
220 return false;
221}
222#endif /* unused */
223
224
225/**
226 * This helper function that validate if client want to operate in supported by server mode.
227 * @param pcTftpHeader comulative header (IP, UDP, TFTP)
228 * @param pcu8Options pointer to the options supposing that pointer points at the mode option
229 * @param cbOptions size of the options buffer
230 */
231DECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
232{
233 AssertPtrReturn(pcTftpSession, 0);
234 return (pcTftpSession->enmTftpFmt == TFTPFMT_OCTET);
235}
236
237
238DECLINLINE(void) tftpSessionUpdate(PNATState pData, PTFTPSESSION pTftpSession)
239{
240 pTftpSession->iTimestamp = curtime;
241 pTftpSession->fInUse = 1;
242}
243
244DECLINLINE(void) tftpSessionTerminate(PTFTPSESSION pTftpSession)
245{
246 pTftpSession->fInUse = 0;
247}
248
249DECLINLINE(int) tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
250{
251 int rc = VINF_SUCCESS;
252 rc = RTStrToInt64Full(pcszRawOption, 0, (int64_t *)&pTftpSessionOption->u64Value);
253 AssertRCReturn(rc, rc);
254 pTftpSessionOption->fRequested = 1;
255 return rc;
256}
257
258DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
259{
260 int rc = VINF_SUCCESS;
261 char *pszTftpRRQRaw;
262 size_t idxTftpRRQRaw = 0;
263 ssize_t cbTftpRRQRaw = 0;
264 int fWithArg = 0;
265 int idxOptionArg = 0;
266
267 AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
268 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
269 AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
270 LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
271
272 pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
273 cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_UOFFSETOF(TFTPIPHDR, Core);
274 while (cbTftpRRQRaw)
275 {
276 rc = RTStrNLenEx(pszTftpRRQRaw, cbTftpRRQRaw, &idxTftpRRQRaw);
277 if (RT_SUCCESS(rc))
278 ++idxTftpRRQRaw; /* count the NUL too */
279 else
280 break;
281
282 if (RTStrNLen((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX) == 0)
283 {
284 rc = RTStrCopy((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
285 if (RT_FAILURE(rc))
286 {
287 LogFlowFuncLeaveRC(rc);
288 AssertRCReturn(rc,rc);
289 }
290 }
291 else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
292 {
293 int idxFmt = 0;
294 rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
295 if (RT_FAILURE(rc))
296 {
297 LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
298 return VERR_INTERNAL_ERROR;
299 }
300 AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
301 && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
302 pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
303 }
304 else if (fWithArg)
305 {
306 if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
307 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
308
309 if ( RT_SUCCESS(rc)
310 && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
311 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize);
312
313 /** @todo we don't use timeout, but its value in the range 0-255 */
314 if ( RT_SUCCESS(rc)
315 && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName))
316 rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout);
317
318 /** @todo unknown option detection */
319 if (RT_FAILURE(rc))
320 {
321 LogFlowFuncLeaveRC(rc);
322 AssertRCReturn(rc,rc);
323 }
324 fWithArg = 0;
325 idxOptionArg = 0;
326 }
327 else
328 {
329 rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
330 if (RT_SUCCESS(rc))
331 fWithArg = 1;
332 else
333 {
334 LogFlowFuncLeaveRC(rc);
335 AssertRCReturn(rc,rc);
336 }
337 }
338 pszTftpRRQRaw += idxTftpRRQRaw;
339 cbTftpRRQRaw -= idxTftpRRQRaw;
340 }
341
342 LogFlowFuncLeaveRC(rc);
343 return rc;
344}
345
346static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
347{
348 PTFTPSESSION pTftpSession = NULL;
349 int rc = VINF_SUCCESS;
350 int idxSession;
351 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
352 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
353 AssertPtrReturn(ppTftpSession, VERR_INVALID_PARAMETER);
354
355 for (idxSession = 0; idxSession < TFTP_SESSIONS_MAX; idxSession++)
356 {
357 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxSession];
358
359 if (!pTftpSession->fInUse)
360 goto found;
361
362 /* sessions time out after 5 inactive seconds */
363 if ((int)(curtime - pTftpSession->iTimestamp) > 5000)
364 goto found;
365 }
366
367 return VERR_NOT_FOUND;
368
369 found:
370 memset(pTftpSession, 0, sizeof(*pTftpSession));
371 memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
372 pTftpSession->u16ClientPort = pcTftpIpHeader->UdpHdr.uh_sport;
373 rc = tftpSessionOptionParse(pTftpSession, pcTftpIpHeader);
374 AssertRCReturn(rc, VERR_INTERNAL_ERROR);
375 *ppTftpSession = pTftpSession;
376
377 tftpSessionUpdate(pData, pTftpSession);
378
379 return VINF_SUCCESS;
380}
381
382static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSessions)
383{
384 PTFTPSESSION pTftpSession;
385 int idxTftpSession;
386 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
387 AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
388 AssertPtrReturn(ppTftpSessions, VERR_INVALID_PARAMETER);
389
390 for (idxTftpSession = 0; idxTftpSession < TFTP_SESSIONS_MAX; idxTftpSession++)
391 {
392 pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxTftpSession];
393
394 if (pTftpSession->fInUse)
395 {
396 if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
397 {
398 if (pTftpSession->u16ClientPort == pcTftpIpHeader->UdpHdr.uh_sport)
399 {
400 *ppTftpSessions = pTftpSession;
401 return VINF_SUCCESS;
402 }
403 }
404 }
405 }
406
407 return VERR_NOT_FOUND;
408}
409
410DECLINLINE(int) pftpSessionOpenFile(PNATState pData, PTFTPSESSION pTftpSession, bool fVerbose, PRTFILE pSessionFile)
411{
412 char szSessionFilename[TFTP_FILENAME_MAX];
413 ssize_t cchSessionFilename;
414 int rc;
415 LogFlowFuncEnter();
416
417 cchSessionFilename = RTStrPrintf2(szSessionFilename, TFTP_FILENAME_MAX, "%s/%s", tftp_prefix, pTftpSession->pszFilename);
418 if (cchSessionFilename > 0)
419 {
420 LogFunc(("szSessionFilename: %s\n", szSessionFilename));
421 if (RTFileExists(szSessionFilename))
422 {
423 rc = RTFileOpen(pSessionFile, szSessionFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
424 }
425 else
426 rc = VERR_FILE_NOT_FOUND;
427 }
428 else
429 rc = VERR_FILENAME_TOO_LONG;
430 if (fVerbose)
431 LogRel(("NAT TFTP: %s/%s -> %Rrc\n", tftp_prefix, pTftpSession->pszFilename, rc));
432 LogFlowFuncLeaveRC(rc);
433 return rc;
434}
435
436DECLINLINE(int) tftpSessionEvaluateOptions(PNATState pData, PTFTPSESSION pTftpSession)
437{
438 int rc;
439 RTFILE hSessionFile;
440 uint64_t cbSessionFile = 0;
441 int cOptions;
442 LogFlowFunc(("pTftpSession:%p\n", pTftpSession));
443
444 rc = pftpSessionOpenFile(pData, pTftpSession, true /*fVerbose*/, &hSessionFile);
445 if (RT_FAILURE(rc))
446 {
447 LogFlowFuncLeaveRC(rc);
448 return rc;
449 }
450
451 rc = RTFileGetSize(hSessionFile, &cbSessionFile);
452 RTFileClose(hSessionFile);
453 if (RT_FAILURE(rc))
454 {
455 LogFlowFuncLeaveRC(rc);
456 return rc;
457 }
458
459 cOptions = 0;
460
461 if (pTftpSession->OptionTSize.fRequested)
462 {
463 pTftpSession->OptionTSize.u64Value = cbSessionFile;
464 ++cOptions;
465 }
466
467 if (pTftpSession->OptionBlkSize.fRequested)
468 {
469 if (pTftpSession->OptionBlkSize.u64Value < 8)
470 {
471 /*
472 * we cannot make a counter-offer larger than the client's
473 * value, so just pretend we didn't recognize it and use
474 * default block size
475 */
476 pTftpSession->OptionBlkSize.fRequested = 0;
477 pTftpSession->OptionBlkSize.u64Value = 512;
478 }
479 else if (pTftpSession->OptionBlkSize.u64Value > 1428)
480 {
481 pTftpSession->OptionBlkSize.u64Value = 1428;
482 ++cOptions;
483 }
484 }
485 else
486 {
487 pTftpSession->OptionBlkSize.u64Value = 512;
488 }
489
490 rc = cOptions > 0 ? VINF_SUCCESS : VWRN_NOT_FOUND;
491 LogFlowFuncLeaveRC(rc);
492 return rc;
493}
494
495DECLINLINE(int) tftpSend(PNATState pData,
496 PTFTPSESSION pTftpSession,
497 struct mbuf *pMBuf,
498 PCTFTPIPHDR pcTftpIpHeaderRecv)
499{
500 int rc = VINF_SUCCESS;
501 struct sockaddr_in saddr, daddr;
502 LogFlowFunc(("pMBuf:%p, pcTftpIpHeaderRecv:%p\n", pMBuf, pcTftpIpHeaderRecv));
503 saddr.sin_addr = pcTftpIpHeaderRecv->IPv4Hdr.ip_dst;
504 saddr.sin_port = pcTftpIpHeaderRecv->UdpHdr.uh_dport;
505
506 daddr.sin_addr = pTftpSession->IpClientAddress;
507 daddr.sin_port = pTftpSession->u16ClientPort;
508
509
510 pMBuf->m_data += sizeof(struct udpiphdr);
511 pMBuf->m_len -= sizeof(struct udpiphdr);
512 udp_output2(pData, NULL, pMBuf, &saddr, &daddr, IPTOS_LOWDELAY);
513 LogFlowFuncLeaveRC(rc);
514 return rc;
515}
516
517
518DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode,
519 const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv); /* gee wiz */
520
521DECLINLINE(int) tftpReadDataBlock(PNATState pData,
522 PTFTPSESSION pcTftpSession,
523 uint8_t *pu8Data,
524 int *pcbReadData)
525{
526 RTFILE hSessionFile;
527 int rc = VINF_SUCCESS;
528 uint16_t u16BlkSize = 0;
529 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
530 AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
531 AssertPtrReturn(pu8Data, VERR_INVALID_PARAMETER);
532 AssertPtrReturn(pcbReadData, VERR_INVALID_PARAMETER);
533 LogFlowFunc(("pcTftpSession:%p, pu8Data:%p, pcbReadData:%p\n",
534 pcTftpSession,
535 pu8Data,
536 pcbReadData));
537
538 u16BlkSize = (uint16_t)pcTftpSession->OptionBlkSize.u64Value;
539 rc = pftpSessionOpenFile(pData, pcTftpSession, false /*fVerbose*/, &hSessionFile);
540 if (RT_FAILURE(rc))
541 {
542 LogFlowFuncLeaveRC(rc);
543 return rc;
544 }
545
546 if (pcbReadData)
547 {
548 size_t cbRead;
549
550 rc = RTFileSeek(hSessionFile,
551 pcTftpSession->cbTransfered,
552 RTFILE_SEEK_BEGIN,
553 NULL);
554 if (RT_FAILURE(rc))
555 {
556 RTFileClose(hSessionFile);
557 LogFlowFuncLeaveRC(rc);
558 return rc;
559 }
560 rc = RTFileRead(hSessionFile, pu8Data, u16BlkSize, &cbRead);
561 if (RT_FAILURE(rc))
562 {
563 RTFileClose(hSessionFile);
564 LogFlowFuncLeaveRC(rc);
565 return rc;
566 }
567 *pcbReadData = (int)cbRead;
568 }
569
570 rc = RTFileClose(hSessionFile);
571
572 LogFlowFuncLeaveRC(rc);
573 return rc;
574}
575
576DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint64_t u64OptValue)
577{
578 char szOptionBuffer[256];
579 size_t iOptLength;
580 int rc = VINF_SUCCESS;
581 int cbMBufCurrent = pMBuf->m_len;
582 LogFlowFunc(("pMBuf:%p, pszOptName:%s, u16OptValue:%ld\n", pMBuf, pszOptName, u64OptValue));
583 AssertPtrReturn(pMBuf, VERR_INVALID_PARAMETER);
584 AssertPtrReturn(pszOptName, VERR_INVALID_PARAMETER);
585
586 RT_ZERO(szOptionBuffer);
587 iOptLength = RTStrPrintf(szOptionBuffer, 256 , "%s", pszOptName) + 1;
588 iOptLength += RTStrPrintf(szOptionBuffer + iOptLength, 256 - iOptLength , "%llu", u64OptValue) + 1;
589 if (iOptLength > M_TRAILINGSPACE(pMBuf))
590 rc = VERR_BUFFER_OVERFLOW; /* buffer too small */
591 else
592 {
593 pMBuf->m_len += (int)iOptLength;
594 m_copyback(pData, pMBuf, cbMBufCurrent, (int)iOptLength, szOptionBuffer);
595 }
596 LogFlowFuncLeaveRC(rc);
597 return rc;
598}
599
600DECLINLINE(int) tftpSendOACK(PNATState pData,
601 PTFTPSESSION pTftpSession,
602 PCTFTPIPHDR pcTftpIpHeaderRecv)
603{
604 struct mbuf *m;
605 PTFTPIPHDR pTftpIpHeader;
606 int rc;
607
608 rc = tftpSessionEvaluateOptions(pData, pTftpSession);
609 if (RT_FAILURE(rc))
610 {
611 tftpSendError(pData, pTftpSession, 2, "Option negotiation failure (file not found or inaccessible?)", pcTftpIpHeaderRecv);
612 LogFlowFuncLeave();
613 return -1;
614 }
615
616 m = slirpTftpMbufAlloc(pData);
617 if (!m)
618 return -1;
619
620 m->m_data += if_maxlinkhdr;
621 m->m_pkthdr.header = mtod(m, void *);
622 pTftpIpHeader = mtod(m, PTFTPIPHDR);
623 m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */
624
625 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK);
626
627 if (pTftpSession->OptionBlkSize.fRequested)
628 rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->OptionBlkSize.u64Value);
629
630 if ( RT_SUCCESS(rc)
631 && pTftpSession->OptionTSize.fRequested)
632 rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->OptionTSize.u64Value);
633
634 rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
635 return RT_SUCCESS(rc) ? 0 : -1;
636}
637
638DECLINLINE(int) tftpSendError(PNATState pData,
639 PTFTPSESSION pTftpSession,
640 uint16_t errorcode,
641 const char *msg,
642 PCTFTPIPHDR pcTftpIpHeaderRecv)
643{
644 struct mbuf *m = NULL;
645 PTFTPIPHDR pTftpIpHeader = NULL;
646 u_int cbMsg = (u_int)strlen(msg) + 1; /* ending zero */
647
648 LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg));
649 m = slirpTftpMbufAlloc(pData);
650 if (!m)
651 {
652 LogFlowFunc(("LEAVE: Can't allocate mbuf\n"));
653 return -1;
654 }
655
656 m->m_data += if_maxlinkhdr;
657 m->m_len = sizeof(TFTPIPHDR) + cbMsg;
658 m->m_pkthdr.header = mtod(m, void *);
659 pTftpIpHeader = mtod(m, PTFTPIPHDR);
660
661 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
662 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);
663
664 m_copyback(pData, m, sizeof(TFTPIPHDR), cbMsg, (c_caddr_t)msg);
665
666 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
667
668 tftpSessionTerminate(pTftpSession);
669
670 LogFlowFuncLeave();
671 return 0;
672}
673
674static int tftpSendData(PNATState pData,
675 PTFTPSESSION pTftpSession,
676 uint16_t u16Block,
677 PCTFTPIPHDR pcTftpIpHeaderRecv)
678{
679 struct mbuf *m;
680 PTFTPIPHDR pTftpIpHeader;
681 int cbRead = 0;
682 int rc = VINF_SUCCESS;
683
684 if (u16Block == pTftpSession->cTftpAck)
685 pTftpSession->cTftpAck++;
686 else
687 {
688 tftpSendError(pData, pTftpSession, 6, "ACK is wrong", pcTftpIpHeaderRecv);
689 tftpSessionTerminate(pTftpSession);
690 return -1;
691 }
692
693 m = slirpTftpMbufAlloc(pData);
694 if (!m)
695 return -1;
696
697 m->m_data += if_maxlinkhdr;
698 m->m_pkthdr.header = mtod(m, void *);
699 pTftpIpHeader = mtod(m, PTFTPIPHDR);
700 m->m_len = sizeof(TFTPIPHDR);
701
702 pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
703 pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck);
704
705 if (RT_LIKELY(M_TRAILINGSPACE(m) >= pTftpSession->OptionBlkSize.u64Value))
706 {
707 uint8_t *pu8Data = (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t);
708 rc = tftpReadDataBlock(pData, pTftpSession, pu8Data, &cbRead);
709 }
710 else
711 rc = VERR_BUFFER_OVERFLOW;
712
713 if (RT_SUCCESS(rc))
714 {
715 pTftpSession->cbTransfered += cbRead;
716 m->m_len += cbRead;
717 tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
718 if (cbRead > 0)
719 tftpSessionUpdate(pData, pTftpSession);
720 else
721 tftpSessionTerminate(pTftpSession);
722 }
723 else
724 {
725 m_freem(pData, m);
726 tftpSendError(pData, pTftpSession, 1, "File not found", pcTftpIpHeaderRecv);
727 /* send "file not found" error back */
728 return -1;
729 }
730
731 return 0;
732}
733
734DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen)
735{
736 PTFTPSESSION pTftpSession = NULL;
737 uint8_t *pu8Payload = NULL;
738 int cbPayload = 0;
739 size_t cbFileName = 0;
740 int rc = VINF_SUCCESS;
741
742 AssertPtrReturnVoid(pTftpIpHeader);
743 AssertPtrReturnVoid(pData);
744 AssertReturnVoid(pktlen > sizeof(TFTPIPHDR));
745 LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen));
746
747 rc = tftpAllocateSession(pData, pTftpIpHeader, &pTftpSession);
748 if ( RT_FAILURE(rc)
749 || pTftpSession == NULL)
750 {
751 LogFlowFuncLeave();
752 return;
753 }
754
755 pu8Payload = (uint8_t *)&pTftpIpHeader->Core;
756 cbPayload = pktlen - sizeof(TFTPIPHDR);
757
758 cbFileName = RTStrNLen((char *)pu8Payload, cbPayload);
759 /* We assume that file name should finish with '\0' and shouldn't bigger
760 * than buffer for name storage.
761 */
762 AssertReturnVoid( (ssize_t)cbFileName < cbPayload
763 && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */
764 && cbFileName);
765
766 /* Dont't bother with rest processing in case of invalid access */
767 if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession)))
768 {
769 tftpSendError(pData, pTftpSession, 2, "Access violation", pTftpIpHeader);
770 LogFlowFuncLeave();
771 return;
772 }
773
774
775
776 if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession)))
777 {
778 tftpSendError(pData, pTftpSession, 4, "Unsupported transfer mode", pTftpIpHeader);
779 LogFlowFuncLeave();
780 return;
781 }
782
783
784 tftpSendOACK(pData, pTftpSession, pTftpIpHeader);
785 LogFlowFuncLeave();
786 return;
787}
788
789static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader)
790{
791 int rc;
792 PTFTPSESSION pTftpSession = NULL;
793
794 rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
795 if (RT_FAILURE(rc))
796 return;
797
798 if (tftpSendData(pData, pTftpSession,
799 RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode),
800 pTftpIpHeader))
801 LogRel(("NAT TFTP: failure\n"));
802}
803
804int slirpTftpInit(PNATState pData)
805{
806 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
807 pData->pvTftpSessions = RTMemAllocZ(sizeof(TFTPSESSION) * TFTP_SESSIONS_MAX);
808 AssertPtrReturn(pData->pvTftpSessions, VERR_NO_MEMORY);
809 return VINF_SUCCESS;
810}
811
812void slirpTftpTerm(PNATState pData)
813{
814 RTMemFree(pData->pvTftpSessions);
815}
816
817int slirpTftpInput(PNATState pData, struct mbuf *pMbuf)
818{
819 PTFTPIPHDR pTftpIpHeader = NULL;
820 AssertPtr(pData);
821 AssertPtr(pMbuf);
822 pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR);
823
824 switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType))
825 {
826 case TFTP_RRQ:
827 tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL));
828 break;
829
830 case TFTP_ACK:
831 tftpProcessACK(pData, pTftpIpHeader);
832 break;
833 default:;
834 }
835 LogFlowFuncLeaveRC(VINF_SUCCESS);
836 return VINF_SUCCESS;
837}
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