VirtualBox

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

Last change on this file since 49482 was 45652, checked in by vboxsync, 12 years ago

slirp/tftp.c: better error message if option negotiation fails (often because the requested file doesn't exist), especially in combination with Intel PXE, plus cleanups

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