VirtualBox

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

Last change on this file since 80986 was 80986, checked in by vboxsync, 5 years ago

NAT: Terminate the TFTP session if the client sends us an error,
otherwise we will be confused if the client initiates the new session
from the same port.

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