VirtualBox

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

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

NAT: typo.

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