VirtualBox

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

Last change on this file since 41996 was 41996, checked in by vboxsync, 13 years ago

NAT: enhances TFTP option handling.

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