VirtualBox

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

Last change on this file since 28449 was 28449, checked in by vboxsync, 15 years ago

NAT: slirp file headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.4 KB
Line 
1/* $Id: tftp.c 28449 2010-04-19 09:52:59Z vboxsync $ */
2/** @file
3 * NAT - TFTP server.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*
23 * This code is based on:
24 *
25 * tftp.c - a simple, read-only tftp server for qemu
26 *
27 * Copyright (c) 2004 Magnus Damm <[email protected]>
28 *
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
35 *
36 * The above copyright notice and this permission notice shall be included in
37 * all copies or substantial portions of the Software.
38 *
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
42 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45 * THE SOFTWARE.
46 */
47
48#include <slirp.h>
49
50
51static void tftp_session_update(PNATState pData, struct tftp_session *spt)
52{
53 spt->timestamp = curtime;
54 spt->in_use = 1;
55}
56
57static void tftp_session_terminate(struct tftp_session *spt)
58{
59 spt->in_use = 0;
60}
61
62static int tftp_session_allocate(PNATState pData, struct tftp_t *tp)
63{
64 struct tftp_session *spt;
65 int k;
66
67 for (k = 0; k < TFTP_SESSIONS_MAX; k++)
68 {
69 spt = &tftp_sessions[k];
70
71 if (!spt->in_use)
72 goto found;
73
74 /* sessions time out after 5 inactive seconds */
75 if ((int)(curtime - spt->timestamp) > 5000)
76 goto found;
77 }
78
79 return -1;
80
81 found:
82 memset(spt, 0, sizeof(*spt));
83 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
84 spt->client_port = tp->udp.uh_sport;
85
86 tftp_session_update(pData, spt);
87
88 return k;
89}
90
91static int tftp_session_find(PNATState pData, struct tftp_t *tp)
92{
93 struct tftp_session *spt;
94 int k;
95
96 for (k = 0; k < TFTP_SESSIONS_MAX; k++)
97 {
98 spt = &tftp_sessions[k];
99
100 if (spt->in_use)
101 {
102 if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)))
103 {
104 if (spt->client_port == tp->udp.uh_sport)
105 return k;
106 }
107 }
108 }
109
110 return -1;
111}
112
113static int tftp_read_data(PNATState pData, struct tftp_session *spt, u_int16_t block_nr,
114 u_int8_t *buf, int len)
115{
116 int fd;
117 int bytes_read = 0;
118 char buffer[1024];
119 int n;
120
121 n = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
122 tftp_prefix, spt->filename);
123 if (n >= sizeof(buffer))
124 return -1;
125
126 fd = open(buffer, O_RDONLY | O_BINARY);
127 if (fd < 0)
128 return -1;
129
130 if (len)
131 {
132 lseek(fd, block_nr * 512, SEEK_SET);
133 bytes_read = read(fd, buf, len);
134 }
135
136 close(fd);
137
138 return bytes_read;
139}
140
141static int tftp_send_oack(PNATState pData,
142 struct tftp_session *spt,
143 const char *key, uint32_t value,
144 struct tftp_t *recv_tp)
145{
146 struct sockaddr_in saddr, daddr;
147 struct mbuf *m;
148 struct tftp_t *tp;
149 int n = 0;
150
151#ifndef VBOX_WITH_SLIRP_BSD_MBUF
152 m = m_get(pData);
153#else
154 m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
155#endif
156 if (!m)
157 return -1;
158
159#ifndef VBOX_WITH_SLIRP_BSD_MBUF
160 memset(m->m_data, 0, m->m_size);
161 m->m_data += if_maxlinkhdr;
162#else
163 m->m_pkthdr.header = mtod(m, void *);
164#endif
165 tp = (void *)m->m_data;
166 m->m_data += sizeof(struct udpiphdr);
167
168 tp->tp_op = RT_H2N_U16_C(TFTP_OACK);
169#ifndef VBOX_WITH_SLIRP_BSD_MBUF
170 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%s", key) + 1;
171 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%u", value) + 1;
172#else
173 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%s", key) + 1;
174 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%u", value) + 1;
175#endif
176
177 saddr.sin_addr = recv_tp->ip.ip_dst;
178 saddr.sin_port = recv_tp->udp.uh_dport;
179
180 daddr.sin_addr = spt->client_ip;
181 daddr.sin_port = spt->client_port;
182
183 m->m_len = sizeof(struct tftp_t) - 514 + n -
184 sizeof(struct ip) - sizeof(struct udphdr);
185 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
186
187 return 0;
188}
189
190static int tftp_send_error(PNATState pData,
191 struct tftp_session *spt,
192 u_int16_t errorcode, const char *msg,
193 struct tftp_t *recv_tp)
194{
195 struct sockaddr_in saddr, daddr;
196 struct mbuf *m;
197 struct tftp_t *tp;
198 int nobytes;
199
200#ifndef VBOX_WITH_SLIRP_BSD_MBUF
201 m = m_get(pData);
202#else
203 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
204#endif
205 if (!m)
206 return -1;
207
208#ifndef VBOX_WITH_SLIRP_BSD_MBUF
209 memset(m->m_data, 0, m->m_size);
210 m->m_data += if_maxlinkhdr;
211#else
212 m->m_pkthdr.header = mtod(m, void *);
213#endif
214 tp = (void *)m->m_data;
215 m->m_data += sizeof(struct udpiphdr);
216
217 tp->tp_op = RT_H2N_U16_C(TFTP_ERROR);
218 tp->x.tp_error.tp_error_code = RT_H2N_U16(errorcode);
219 strcpy((char *)tp->x.tp_error.tp_msg, msg);
220
221 saddr.sin_addr = recv_tp->ip.ip_dst;
222 saddr.sin_port = recv_tp->udp.uh_dport;
223
224 daddr.sin_addr = spt->client_ip;
225 daddr.sin_port = spt->client_port;
226
227 nobytes = 2;
228
229 m->m_len = sizeof(struct tftp_t)
230 - 514
231 + 3
232 + strlen(msg)
233 - sizeof(struct ip)
234 - sizeof(struct udphdr);
235
236 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
237
238 tftp_session_terminate(spt);
239
240 return 0;
241}
242
243static int tftp_send_data(PNATState pData,
244 struct tftp_session *spt,
245 u_int16_t block_nr,
246 struct tftp_t *recv_tp)
247{
248 struct sockaddr_in saddr, daddr;
249 struct mbuf *m;
250 struct tftp_t *tp;
251 int nobytes;
252
253 if (block_nr < 1)
254 return -1;
255
256#ifndef VBOX_WITH_SLIRP_BSD_MBUF
257 m = m_get(pData);
258#else
259 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
260#endif
261 if (!m)
262 return -1;
263
264#ifndef VBOX_WITH_SLIRP_BSD_MBUF
265 memset(m->m_data, 0, m->m_size);
266 m->m_data += if_maxlinkhdr;
267#else
268 m->m_pkthdr.header = mtod(m, void *);
269#endif
270 tp = (void *)m->m_data;
271 m->m_data += sizeof(struct udpiphdr);
272
273 tp->tp_op = RT_H2N_U16_C(TFTP_DATA);
274 tp->x.tp_data.tp_block_nr = RT_H2N_U16(block_nr);
275
276 saddr.sin_addr = recv_tp->ip.ip_dst;
277 saddr.sin_port = recv_tp->udp.uh_dport;
278
279 daddr.sin_addr = spt->client_ip;
280 daddr.sin_port = spt->client_port;
281
282 nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
283 if (nobytes < 0)
284 {
285 m_freem(pData, m);
286 /* send "file not found" error back */
287 tftp_send_error(pData, spt, 1, "File not found", tp);
288 return -1;
289 }
290
291 m->m_len = sizeof(struct tftp_t)
292 - (512 - nobytes)
293 - sizeof(struct ip)
294 - sizeof(struct udphdr);
295
296 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
297
298 if (nobytes == 512)
299 tftp_session_update(pData, spt);
300 else
301 tftp_session_terminate(spt);
302
303 return 0;
304}
305
306static void tftp_handle_rrq(PNATState pData, struct tftp_t *tp, int pktlen)
307{
308 struct tftp_session *spt;
309 int s, k, n;
310 u_int8_t *src, *dst;
311
312 s = tftp_session_allocate(pData, tp);
313 if (s < 0)
314 return;
315
316 spt = &tftp_sessions[s];
317
318 src = tp->x.tp_buf;
319 dst = spt->filename;
320 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
321
322 /* get name */
323 for (k = 0; k < n; k++)
324 {
325 if (k < TFTP_FILENAME_MAX)
326 dst[k] = src[k];
327 else
328 return;
329
330 if (src[k] == '\0')
331 break;
332 }
333
334 if (k >= n)
335 return;
336
337 k++;
338
339 /* check mode */
340 if ((n - k) < 6)
341 return;
342
343 if (memcmp(&src[k], "octet\0", 6) != 0)
344 {
345 tftp_send_error(pData, spt, 4, "Unsupported transfer mode", tp);
346 return;
347 }
348
349 k += 6; /* skipping octet */
350
351 /* do sanity checks on the filename */
352 if ( !strncmp((const char*)spt->filename, "../", 3)
353 || (spt->filename[strlen((const char *)spt->filename) - 1] == '/')
354 || strstr((const char *)spt->filename, "/../"))
355 {
356 tftp_send_error(pData, spt, 2, "Access violation", tp);
357 return;
358 }
359
360 /* only allow exported prefixes */
361 if (!tftp_prefix)
362 {
363 tftp_send_error(pData, spt, 2, "Access violation", tp);
364 return;
365 }
366
367 /* check if the file exists */
368 if (tftp_read_data(pData, spt, 0, spt->filename, 0) < 0)
369 {
370 tftp_send_error(pData, spt, 1, "File not found", tp);
371 return;
372 }
373
374 if (src[n - 1] != 0)
375 {
376 tftp_send_error(pData, spt, 2, "Access violation", tp);
377 return;
378 }
379
380 while (k < n)
381 {
382 const char *key, *value;
383
384 key = (const char *)src + k;
385 k += strlen(key) + 1;
386
387 if (k >= n)
388 {
389 tftp_send_error(pData, spt, 2, "Access violation", tp);
390 return;
391 }
392
393 value = (const char *)src + k;
394 k += strlen(value) + 1;
395
396 if (strcmp(key, "tsize") == 0)
397 {
398 int tsize = atoi(value);
399 struct stat stat_p;
400
401 if (tsize == 0 && tftp_prefix)
402 {
403 char buffer[1024];
404 int len;
405
406 len = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
407 tftp_prefix, spt->filename);
408 if (stat(buffer, &stat_p) == 0)
409 tsize = stat_p.st_size;
410 else
411 {
412 tftp_send_error(pData, spt, 1, "File not found", tp);
413 return;
414 }
415 }
416
417 tftp_send_oack(pData, spt, "tsize", tsize, tp);
418 return;
419 }
420 }
421
422 tftp_send_data(pData, spt, 1, tp);
423}
424
425static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen)
426{
427 int s;
428
429 s = tftp_session_find(pData, tp);
430 if (s < 0)
431 return;
432
433 if (tftp_send_data(pData, &tftp_sessions[s],
434 RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0)
435 {
436 /* XXX */
437 }
438}
439
440void tftp_input(PNATState pData, struct mbuf *m)
441{
442 struct tftp_t *tp = (struct tftp_t *)m->m_data;
443
444 switch(RT_N2H_U16(tp->tp_op))
445 {
446 case TFTP_RRQ:
447 tftp_handle_rrq(pData, tp, m->m_len);
448 break;
449
450 case TFTP_ACK:
451 tftp_handle_ack(pData, tp, m->m_len);
452 break;
453 }
454}
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