VirtualBox

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

Last change on this file since 69304 was 66828, checked in by vboxsync, 8 years ago

Devices: Adjustments for Visual C++ v12 / Visual Studio 2013. New warnings.

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