VirtualBox

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

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

NAT: reserves space for ether header before any manipulation with mbuf.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.3 KB
Line 
1/* $Id: tftp.c 29050 2010-05-05 02:26:10Z 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
46
47static void tftp_session_update(PNATState pData, struct tftp_session *spt)
48{
49 spt->timestamp = curtime;
50 spt->in_use = 1;
51}
52
53static void tftp_session_terminate(struct tftp_session *spt)
54{
55 spt->in_use = 0;
56}
57
58static int tftp_session_allocate(PNATState pData, struct tftp_t *tp)
59{
60 struct tftp_session *spt;
61 int k;
62
63 for (k = 0; k < TFTP_SESSIONS_MAX; k++)
64 {
65 spt = &tftp_sessions[k];
66
67 if (!spt->in_use)
68 goto found;
69
70 /* sessions time out after 5 inactive seconds */
71 if ((int)(curtime - spt->timestamp) > 5000)
72 goto found;
73 }
74
75 return -1;
76
77 found:
78 memset(spt, 0, sizeof(*spt));
79 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
80 spt->client_port = tp->udp.uh_sport;
81
82 tftp_session_update(pData, spt);
83
84 return k;
85}
86
87static int tftp_session_find(PNATState pData, struct tftp_t *tp)
88{
89 struct tftp_session *spt;
90 int k;
91
92 for (k = 0; k < TFTP_SESSIONS_MAX; k++)
93 {
94 spt = &tftp_sessions[k];
95
96 if (spt->in_use)
97 {
98 if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)))
99 {
100 if (spt->client_port == tp->udp.uh_sport)
101 return k;
102 }
103 }
104 }
105
106 return -1;
107}
108
109static int tftp_read_data(PNATState pData, struct tftp_session *spt, u_int16_t block_nr,
110 u_int8_t *buf, int len)
111{
112 int fd;
113 int bytes_read = 0;
114 char buffer[1024];
115 int n;
116
117 n = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
118 tftp_prefix, spt->filename);
119 if (n >= sizeof(buffer))
120 return -1;
121
122 fd = open(buffer, O_RDONLY | O_BINARY);
123 if (fd < 0)
124 return -1;
125
126 if (len)
127 {
128 lseek(fd, block_nr * 512, SEEK_SET);
129 bytes_read = read(fd, buf, len);
130 }
131
132 close(fd);
133
134 return bytes_read;
135}
136
137static int tftp_send_oack(PNATState pData,
138 struct tftp_session *spt,
139 const char *key, uint32_t value,
140 struct tftp_t *recv_tp)
141{
142 struct sockaddr_in saddr, daddr;
143 struct mbuf *m;
144 struct tftp_t *tp;
145 int n = 0;
146
147#ifndef VBOX_WITH_SLIRP_BSD_MBUF
148 m = m_get(pData);
149#else
150 m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
151#endif
152 if (!m)
153 return -1;
154
155#ifndef VBOX_WITH_SLIRP_BSD_MBUF
156 memset(m->m_data, 0, m->m_size);
157 m->m_data += if_maxlinkhdr;
158#else
159 m->m_data += if_maxlinkhdr;
160 m->m_pkthdr.header = mtod(m, void *);
161#endif
162 tp = (void *)m->m_data;
163 m->m_data += sizeof(struct udpiphdr);
164
165 tp->tp_op = RT_H2N_U16_C(TFTP_OACK);
166#ifndef VBOX_WITH_SLIRP_BSD_MBUF
167 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%s", key) + 1;
168 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%u", value) + 1;
169#else
170 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%s", key) + 1;
171 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%u", value) + 1;
172#endif
173
174 saddr.sin_addr = recv_tp->ip.ip_dst;
175 saddr.sin_port = recv_tp->udp.uh_dport;
176
177 daddr.sin_addr = spt->client_ip;
178 daddr.sin_port = spt->client_port;
179
180 m->m_len = sizeof(struct tftp_t) - 514 + n -
181 sizeof(struct ip) - sizeof(struct udphdr);
182 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
183
184 return 0;
185}
186
187static int tftp_send_error(PNATState pData,
188 struct tftp_session *spt,
189 u_int16_t errorcode, const char *msg,
190 struct tftp_t *recv_tp)
191{
192 struct sockaddr_in saddr, daddr;
193 struct mbuf *m;
194 struct tftp_t *tp;
195 int nobytes;
196
197#ifndef VBOX_WITH_SLIRP_BSD_MBUF
198 m = m_get(pData);
199#else
200 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
201#endif
202 if (!m)
203 return -1;
204
205#ifndef VBOX_WITH_SLIRP_BSD_MBUF
206 memset(m->m_data, 0, m->m_size);
207 m->m_data += if_maxlinkhdr;
208#else
209 m->m_data += if_maxlinkhdr;
210 m->m_pkthdr.header = mtod(m, void *);
211#endif
212 tp = (void *)m->m_data;
213 m->m_data += sizeof(struct udpiphdr);
214
215 tp->tp_op = RT_H2N_U16_C(TFTP_ERROR);
216 tp->x.tp_error.tp_error_code = RT_H2N_U16(errorcode);
217 strcpy((char *)tp->x.tp_error.tp_msg, msg);
218
219 saddr.sin_addr = recv_tp->ip.ip_dst;
220 saddr.sin_port = recv_tp->udp.uh_dport;
221
222 daddr.sin_addr = spt->client_ip;
223 daddr.sin_port = spt->client_port;
224
225 nobytes = 2;
226
227 m->m_len = sizeof(struct tftp_t)
228 - 514
229 + 3
230 + strlen(msg)
231 - sizeof(struct ip)
232 - sizeof(struct udphdr);
233
234 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
235
236 tftp_session_terminate(spt);
237
238 return 0;
239}
240
241static int tftp_send_data(PNATState pData,
242 struct tftp_session *spt,
243 u_int16_t block_nr,
244 struct tftp_t *recv_tp)
245{
246 struct sockaddr_in saddr, daddr;
247 struct mbuf *m;
248 struct tftp_t *tp;
249 int nobytes;
250
251 if (block_nr < 1)
252 return -1;
253
254#ifndef VBOX_WITH_SLIRP_BSD_MBUF
255 m = m_get(pData);
256#else
257 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
258#endif
259 if (!m)
260 return -1;
261
262#ifndef VBOX_WITH_SLIRP_BSD_MBUF
263 memset(m->m_data, 0, m->m_size);
264 m->m_data += if_maxlinkhdr;
265#else
266 m->m_data += if_maxlinkhdr;
267 m->m_pkthdr.header = mtod(m, void *);
268#endif
269 tp = mtod(m, void *);
270 m->m_data += sizeof(struct udpiphdr);
271
272 tp->tp_op = RT_H2N_U16_C(TFTP_DATA);
273 tp->x.tp_data.tp_block_nr = RT_H2N_U16(block_nr);
274
275 saddr.sin_addr = recv_tp->ip.ip_dst;
276 saddr.sin_port = recv_tp->udp.uh_dport;
277
278 daddr.sin_addr = spt->client_ip;
279 daddr.sin_port = spt->client_port;
280
281 nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
282 if (nobytes < 0)
283 {
284 m_freem(pData, m);
285 /* send "file not found" error back */
286 tftp_send_error(pData, spt, 1, "File not found", tp);
287 return -1;
288 }
289
290 m->m_len = sizeof(struct tftp_t)
291 - (512 - nobytes)
292 - sizeof(struct ip)
293 - sizeof(struct udphdr);
294
295 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
296
297 if (nobytes == 512)
298 tftp_session_update(pData, spt);
299 else
300 tftp_session_terminate(spt);
301
302 return 0;
303}
304
305static void tftp_handle_rrq(PNATState pData, struct tftp_t *tp, int pktlen)
306{
307 struct tftp_session *spt;
308 int s, k, n;
309 u_int8_t *src, *dst;
310
311 s = tftp_session_allocate(pData, tp);
312 if (s < 0)
313 return;
314
315 spt = &tftp_sessions[s];
316
317 src = tp->x.tp_buf;
318 dst = spt->filename;
319 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
320
321 /* get name */
322 for (k = 0; k < n; k++)
323 {
324 if (k < TFTP_FILENAME_MAX)
325 dst[k] = src[k];
326 else
327 return;
328
329 if (src[k] == '\0')
330 break;
331 }
332
333 if (k >= n)
334 return;
335
336 k++;
337
338 /* check mode */
339 if ((n - k) < 6)
340 return;
341
342 if (memcmp(&src[k], "octet\0", 6) != 0)
343 {
344 tftp_send_error(pData, spt, 4, "Unsupported transfer mode", tp);
345 return;
346 }
347
348 k += 6; /* skipping octet */
349
350 /* do sanity checks on the filename */
351 if ( !strncmp((const char*)spt->filename, "../", 3)
352 || (spt->filename[strlen((const char *)spt->filename) - 1] == '/')
353 || strstr((const char *)spt->filename, "/../"))
354 {
355 tftp_send_error(pData, spt, 2, "Access violation", tp);
356 return;
357 }
358
359 /* only allow exported prefixes */
360 if (!tftp_prefix)
361 {
362 tftp_send_error(pData, spt, 2, "Access violation", tp);
363 return;
364 }
365
366 /* check if the file exists */
367 if (tftp_read_data(pData, spt, 0, spt->filename, 0) < 0)
368 {
369 tftp_send_error(pData, spt, 1, "File not found", tp);
370 return;
371 }
372
373 if (src[n - 1] != 0)
374 {
375 tftp_send_error(pData, spt, 2, "Access violation", tp);
376 return;
377 }
378
379 while (k < n)
380 {
381 const char *key, *value;
382
383 key = (const char *)src + k;
384 k += strlen(key) + 1;
385
386 if (k >= n)
387 {
388 tftp_send_error(pData, spt, 2, "Access violation", tp);
389 return;
390 }
391
392 value = (const char *)src + k;
393 k += strlen(value) + 1;
394
395 if (strcmp(key, "tsize") == 0)
396 {
397 int tsize = atoi(value);
398 struct stat stat_p;
399
400 if (tsize == 0 && tftp_prefix)
401 {
402 char buffer[1024];
403 int len;
404
405 len = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
406 tftp_prefix, spt->filename);
407 if (stat(buffer, &stat_p) == 0)
408 tsize = stat_p.st_size;
409 else
410 {
411 tftp_send_error(pData, spt, 1, "File not found", tp);
412 return;
413 }
414 }
415
416 tftp_send_oack(pData, spt, "tsize", tsize, tp);
417 return;
418 }
419 }
420
421 tftp_send_data(pData, spt, 1, tp);
422}
423
424static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen)
425{
426 int s;
427
428 s = tftp_session_find(pData, tp);
429 if (s < 0)
430 return;
431
432 if (tftp_send_data(pData, &tftp_sessions[s],
433 RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0)
434 {
435 /* XXX */
436 }
437}
438
439void tftp_input(PNATState pData, struct mbuf *m)
440{
441 struct tftp_t *tp = (struct tftp_t *)m->m_data;
442
443 switch(RT_N2H_U16(tp->tp_op))
444 {
445 case TFTP_RRQ:
446 tftp_handle_rrq(pData, tp, m->m_len);
447 break;
448
449 case TFTP_ACK:
450 tftp_handle_ack(pData, tp, m->m_len);
451 break;
452 }
453}
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