VirtualBox

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

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

NAT/tftp: bugref:9350 - Fix the type and the hungarian prefix of the
filename array. While here, move it to the end of the structure.

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