VirtualBox

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

Last change on this file since 16254 was 15875, checked in by vboxsync, 16 years ago

If we sent an OACK, wait for ACK from client before sending file data.

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