VirtualBox

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

Last change on this file since 76747 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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