VirtualBox

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

Last change on this file since 30016 was 30016, checked in by vboxsync, 14 years ago

NAT: clean up.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: tftp.c 30016 2010-06-03 18:31:14Z 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 <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
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 m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
148 if (!m)
149 return -1;
150
151 m->m_data += if_maxlinkhdr;
152 m->m_pkthdr.header = mtod(m, void *);
153 tp = (void *)m->m_data;
154 m->m_data += sizeof(struct udpiphdr);
155
156 tp->tp_op = RT_H2N_U16_C(TFTP_OACK);
157 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%s", key) + 1;
158 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_TRAILINGSPACE(m), "%u", value) + 1;
159
160 saddr.sin_addr = recv_tp->ip.ip_dst;
161 saddr.sin_port = recv_tp->udp.uh_dport;
162
163 daddr.sin_addr = spt->client_ip;
164 daddr.sin_port = spt->client_port;
165
166 m->m_len = sizeof(struct tftp_t) - 514 + n -
167 sizeof(struct ip) - sizeof(struct udphdr);
168 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
169
170 return 0;
171}
172
173static int tftp_send_error(PNATState pData,
174 struct tftp_session *spt,
175 u_int16_t errorcode, const char *msg,
176 struct tftp_t *recv_tp)
177{
178 struct sockaddr_in saddr, daddr;
179 struct mbuf *m;
180 struct tftp_t *tp;
181 int nobytes;
182
183 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
184 if (!m)
185 return -1;
186
187 m->m_data += if_maxlinkhdr;
188 m->m_pkthdr.header = mtod(m, void *);
189 tp = (void *)m->m_data;
190 m->m_data += sizeof(struct udpiphdr);
191
192 tp->tp_op = RT_H2N_U16_C(TFTP_ERROR);
193 tp->x.tp_error.tp_error_code = RT_H2N_U16(errorcode);
194 strcpy((char *)tp->x.tp_error.tp_msg, msg);
195
196 saddr.sin_addr = recv_tp->ip.ip_dst;
197 saddr.sin_port = recv_tp->udp.uh_dport;
198
199 daddr.sin_addr = spt->client_ip;
200 daddr.sin_port = spt->client_port;
201
202 nobytes = 2;
203
204 m->m_len = sizeof(struct tftp_t)
205 - 514
206 + 3
207 + strlen(msg)
208 - sizeof(struct ip)
209 - sizeof(struct udphdr);
210
211 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
212
213 tftp_session_terminate(spt);
214
215 return 0;
216}
217
218static int tftp_send_data(PNATState pData,
219 struct tftp_session *spt,
220 u_int16_t block_nr,
221 struct tftp_t *recv_tp)
222{
223 struct sockaddr_in saddr, daddr;
224 struct mbuf *m;
225 struct tftp_t *tp;
226 int nobytes;
227
228 if (block_nr < 1)
229 return -1;
230
231 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
232 if (!m)
233 return -1;
234
235 m->m_data += if_maxlinkhdr;
236 m->m_pkthdr.header = mtod(m, void *);
237 tp = mtod(m, void *);
238 m->m_data += sizeof(struct udpiphdr);
239
240 tp->tp_op = RT_H2N_U16_C(TFTP_DATA);
241 tp->x.tp_data.tp_block_nr = RT_H2N_U16(block_nr);
242
243 saddr.sin_addr = recv_tp->ip.ip_dst;
244 saddr.sin_port = recv_tp->udp.uh_dport;
245
246 daddr.sin_addr = spt->client_ip;
247 daddr.sin_port = spt->client_port;
248
249 nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
250 if (nobytes < 0)
251 {
252 m_freem(pData, m);
253 /* send "file not found" error back */
254 tftp_send_error(pData, spt, 1, "File not found", tp);
255 return -1;
256 }
257
258 m->m_len = sizeof(struct tftp_t)
259 - (512 - nobytes)
260 - sizeof(struct ip)
261 - sizeof(struct udphdr);
262
263 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
264
265 if (nobytes == 512)
266 tftp_session_update(pData, spt);
267 else
268 tftp_session_terminate(spt);
269
270 return 0;
271}
272
273static void tftp_handle_rrq(PNATState pData, struct tftp_t *tp, int pktlen)
274{
275 struct tftp_session *spt;
276 int s, k, n;
277 u_int8_t *src, *dst;
278
279 s = tftp_session_allocate(pData, tp);
280 if (s < 0)
281 return;
282
283 spt = &tftp_sessions[s];
284
285 src = tp->x.tp_buf;
286 dst = spt->filename;
287 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
288
289 /* get name */
290 for (k = 0; k < n; k++)
291 {
292 if (k < TFTP_FILENAME_MAX)
293 dst[k] = src[k];
294 else
295 return;
296
297 if (src[k] == '\0')
298 break;
299 }
300
301 if (k >= n)
302 return;
303
304 k++;
305
306 /* check mode */
307 if ((n - k) < 6)
308 return;
309
310 if (memcmp(&src[k], "octet\0", 6) != 0)
311 {
312 tftp_send_error(pData, spt, 4, "Unsupported transfer mode", tp);
313 return;
314 }
315
316 k += 6; /* skipping octet */
317
318 /* do sanity checks on the filename */
319 if ( !strncmp((const char*)spt->filename, "../", 3)
320 || (spt->filename[strlen((const char *)spt->filename) - 1] == '/')
321 || strstr((const char *)spt->filename, "/../"))
322 {
323 tftp_send_error(pData, spt, 2, "Access violation", tp);
324 return;
325 }
326
327 /* only allow exported prefixes */
328 if (!tftp_prefix)
329 {
330 tftp_send_error(pData, spt, 2, "Access violation", tp);
331 return;
332 }
333
334 /* check if the file exists */
335 if (tftp_read_data(pData, spt, 0, spt->filename, 0) < 0)
336 {
337 tftp_send_error(pData, spt, 1, "File not found", tp);
338 return;
339 }
340
341 if (src[n - 1] != 0)
342 {
343 tftp_send_error(pData, spt, 2, "Access violation", tp);
344 return;
345 }
346
347 while (k < n)
348 {
349 const char *key, *value;
350
351 key = (const char *)src + k;
352 k += strlen(key) + 1;
353
354 if (k >= n)
355 {
356 tftp_send_error(pData, spt, 2, "Access violation", tp);
357 return;
358 }
359
360 value = (const char *)src + k;
361 k += strlen(value) + 1;
362
363 if (strcmp(key, "tsize") == 0)
364 {
365 int tsize = atoi(value);
366 struct stat stat_p;
367
368 if (tsize == 0 && tftp_prefix)
369 {
370 char buffer[1024];
371 int len;
372
373 len = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
374 tftp_prefix, spt->filename);
375 if (stat(buffer, &stat_p) == 0)
376 tsize = stat_p.st_size;
377 else
378 {
379 tftp_send_error(pData, spt, 1, "File not found", tp);
380 return;
381 }
382 }
383
384 tftp_send_oack(pData, spt, "tsize", tsize, tp);
385 return;
386 }
387 }
388
389 tftp_send_data(pData, spt, 1, tp);
390}
391
392static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen)
393{
394 int s;
395
396 s = tftp_session_find(pData, tp);
397 if (s < 0)
398 return;
399
400 if (tftp_send_data(pData, &tftp_sessions[s],
401 RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0)
402 {
403 /* XXX */
404 }
405}
406
407void tftp_input(PNATState pData, struct mbuf *m)
408{
409 struct tftp_t *tp = (struct tftp_t *)m->m_data;
410
411 switch(RT_N2H_U16(tp->tp_op))
412 {
413 case TFTP_RRQ:
414 tftp_handle_rrq(pData, tp, m->m_len);
415 break;
416
417 case TFTP_ACK:
418 tftp_handle_ack(pData, tp, m->m_len);
419 break;
420 }
421}
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