VirtualBox

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

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

NAT: stdio -> IPRT/fileio

  • 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 += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%s", key) + 1;
140 n += RTStrPrintf((char *)tp->x.tp_buf + n, M_FREEROOM(m), "%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