VirtualBox

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

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

NAT: applied patch from xtracker 3993 (use BSD mbufs)

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