VirtualBox

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

Last change on this file since 1033 was 1033, checked in by vboxsync, 18 years ago

Big change to make slirp fully instantiatable (replace all global
variables with local ones, passing a reference to the state/config
structure to all places which are interested). You can now have as many
cards in the guest configured for NAT networking as you want.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 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#ifndef VBOX
28struct tftp_session {
29 int in_use;
30 unsigned char filename[TFTP_FILENAME_MAX];
31
32 struct in_addr client_ip;
33 u_int16_t client_port;
34
35 int timestamp;
36};
37
38struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
39
40const char *tftp_prefix;
41#endif /* !VBOX */
42
43#ifdef VBOX
44static void tftp_session_update(PNATState pData, struct tftp_session *spt)
45#else /* !VBOX */
46static void tftp_session_update(struct tftp_session *spt)
47#endif /* !VBOX */
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
58#ifdef VBOX
59static int tftp_session_allocate(PNATState pData, struct tftp_t *tp)
60#else /* !VBOX */
61static int tftp_session_allocate(struct tftp_t *tp)
62#endif /* !VBOX */
63{
64 struct tftp_session *spt;
65 int k;
66
67 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
68 spt = &tftp_sessions[k];
69
70 if (!spt->in_use)
71 goto found;
72
73 /* sessions time out after 5 inactive seconds */
74 if ((int)(curtime - spt->timestamp) > 5000)
75 goto found;
76 }
77
78 return -1;
79
80 found:
81 memset(spt, 0, sizeof(*spt));
82 memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
83 spt->client_port = tp->udp.uh_sport;
84
85#ifdef VBOX
86 tftp_session_update(pData, spt);
87#else /* !VBOX */
88 tftp_session_update(spt);
89#endif /* !VBOX */
90
91 return k;
92}
93
94#ifdef VBOX
95static int tftp_session_find(PNATState pData, struct tftp_t *tp)
96#else /* !VBOX */
97static int tftp_session_find(struct tftp_t *tp)
98#endif /* !VBOX */
99{
100 struct tftp_session *spt;
101 int k;
102
103 for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
104 spt = &tftp_sessions[k];
105
106 if (spt->in_use) {
107 if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
108 if (spt->client_port == tp->udp.uh_sport) {
109 return k;
110 }
111 }
112 }
113 }
114
115 return -1;
116}
117
118#ifdef VBOX
119static int tftp_read_data(PNATState pData, struct tftp_session *spt, u_int16_t block_nr,
120 u_int8_t *buf, int len)
121#else /* !VBOX */
122static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
123 u_int8_t *buf, int len)
124#endif /* !VBOX */
125{
126 int fd;
127 int bytes_read = 0;
128 char buffer[1024];
129 int n;
130
131#ifndef VBOX
132 n = snprintf(buffer, sizeof(buffer), "%s/%s",
133 tftp_prefix, spt->filename);
134#else
135 n = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
136 tftp_prefix, spt->filename);
137#endif
138 if (n >= sizeof(buffer))
139 return -1;
140
141 fd = open(buffer, O_RDONLY | O_BINARY);
142
143 if (fd < 0) {
144 return -1;
145 }
146
147 if (len) {
148 lseek(fd, block_nr * 512, SEEK_SET);
149
150 bytes_read = read(fd, buf, len);
151 }
152
153 close(fd);
154
155 return bytes_read;
156}
157
158#ifdef VBOX
159static int tftp_send_oack(PNATState pData,
160 struct tftp_session *spt,
161 const char *key, uint32_t value,
162 struct tftp_t *recv_tp)
163#else /* !VBOX */
164static int tftp_send_oack(struct tftp_session *spt,
165 const char *key, uint32_t value,
166 struct tftp_t *recv_tp)
167#endif /* !VBOX */
168{
169 struct sockaddr_in saddr, daddr;
170 struct mbuf *m;
171 struct tftp_t *tp;
172 int n = 0;
173
174#ifdef VBOX
175 m = m_get(pData);
176#else /* !VBOX */
177 m = m_get();
178#endif /* !VBOX */
179
180 if (!m)
181 return -1;
182
183 memset(m->m_data, 0, m->m_size);
184
185 m->m_data += if_maxlinkhdr;
186 tp = (void *)m->m_data;
187 m->m_data += sizeof(struct udpiphdr);
188
189 tp->tp_op = htons(TFTP_OACK);
190#ifdef VBOX
191 n += sprintf((char *)tp->x.tp_buf + n, "%s", key) + 1;
192 n += sprintf((char *)tp->x.tp_buf + n, "%u", value) + 1;
193#else /* !VBOX */
194 n += sprintf(tp->x.tp_buf + n, "%s", key) + 1;
195 n += sprintf(tp->x.tp_buf + n, "%u", value) + 1;
196#endif /* !VBOX */
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 m->m_len = sizeof(struct tftp_t) - 514 + n -
205 sizeof(struct ip) - sizeof(struct udphdr);
206#ifdef VBOX
207 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
208#else /* !VBOX */
209 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
210#endif /* !VBOX */
211
212 return 0;
213}
214
215
216
217#ifdef VBOX
218static int tftp_send_error(PNATState pData,
219 struct tftp_session *spt,
220 u_int16_t errorcode, const char *msg,
221 struct tftp_t *recv_tp)
222#else /* !VBOX */
223static int tftp_send_error(struct tftp_session *spt,
224 u_int16_t errorcode, const char *msg,
225 struct tftp_t *recv_tp)
226#endif /* !VBOX */
227{
228 struct sockaddr_in saddr, daddr;
229 struct mbuf *m;
230 struct tftp_t *tp;
231 int nobytes;
232
233#ifdef VBOX
234 m = m_get(pData);
235#else /* !VBOX */
236 m = m_get();
237#endif /* !VBOX */
238
239 if (!m) {
240 return -1;
241 }
242
243 memset(m->m_data, 0, m->m_size);
244
245 m->m_data += if_maxlinkhdr;
246 tp = (void *)m->m_data;
247 m->m_data += sizeof(struct udpiphdr);
248
249 tp->tp_op = htons(TFTP_ERROR);
250 tp->x.tp_error.tp_error_code = htons(errorcode);
251#ifndef VBOX
252 strcpy(tp->x.tp_error.tp_msg, msg);
253#else /* VBOX */
254 strcpy((char *)tp->x.tp_error.tp_msg, msg);
255#endif /* VBOX */
256
257 saddr.sin_addr = recv_tp->ip.ip_dst;
258 saddr.sin_port = recv_tp->udp.uh_dport;
259
260 daddr.sin_addr = spt->client_ip;
261 daddr.sin_port = spt->client_port;
262
263 nobytes = 2;
264
265 m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
266 sizeof(struct ip) - sizeof(struct udphdr);
267
268#ifdef VBOX
269 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
270#else /* !VBOX */
271 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
272#endif /* !VBOX */
273
274 tftp_session_terminate(spt);
275
276 return 0;
277}
278
279#ifdef VBOX
280static int tftp_send_data(PNATState pData,
281 struct tftp_session *spt,
282 u_int16_t block_nr,
283 struct tftp_t *recv_tp)
284#else /* !VBOX */
285static int tftp_send_data(struct tftp_session *spt,
286 u_int16_t block_nr,
287 struct tftp_t *recv_tp)
288#endif /* !VBOX */
289{
290 struct sockaddr_in saddr, daddr;
291 struct mbuf *m;
292 struct tftp_t *tp;
293 int nobytes;
294
295 if (block_nr < 1) {
296 return -1;
297 }
298
299#ifdef VBOX
300 m = m_get(pData);
301#else /* !VBOX */
302 m = m_get();
303#endif /* !VBOX */
304
305 if (!m) {
306 return -1;
307 }
308
309 memset(m->m_data, 0, m->m_size);
310
311 m->m_data += if_maxlinkhdr;
312 tp = (void *)m->m_data;
313 m->m_data += sizeof(struct udpiphdr);
314
315 tp->tp_op = htons(TFTP_DATA);
316 tp->x.tp_data.tp_block_nr = htons(block_nr);
317
318 saddr.sin_addr = recv_tp->ip.ip_dst;
319 saddr.sin_port = recv_tp->udp.uh_dport;
320
321 daddr.sin_addr = spt->client_ip;
322 daddr.sin_port = spt->client_port;
323
324#ifdef VBOX
325 nobytes = tftp_read_data(pData, spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
326#else /* !VBOX */
327 nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
328#endif /* !VBOX */
329
330 if (nobytes < 0) {
331#ifdef VBOX
332 m_free(pData, m);
333#else /* !VBOX */
334 m_free(m);
335#endif /* !VBOX */
336
337 /* send "file not found" error back */
338
339#ifdef VBOX
340 tftp_send_error(pData, spt, 1, "File not found", tp);
341#else /* !VBOX */
342 tftp_send_error(spt, 1, "File not found", tp);
343#endif /* !VBOX */
344
345 return -1;
346 }
347
348 m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
349 sizeof(struct ip) - sizeof(struct udphdr);
350
351#ifdef VBOX
352 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
353#else /* !VBOX */
354 udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
355#endif /* !VBOX */
356
357 if (nobytes == 512) {
358#ifdef VBOX
359 tftp_session_update(pData, spt);
360#else /* !VBOX */
361 tftp_session_update(spt);
362#endif /* !VBOX */
363 }
364 else {
365 tftp_session_terminate(spt);
366 }
367
368 return 0;
369}
370
371#ifdef VBOX
372static void tftp_handle_rrq(PNATState pData, struct tftp_t *tp, int pktlen)
373#else /* !VBOX */
374static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
375#endif /* !VBOX */
376{
377 struct tftp_session *spt;
378 int s, k, n;
379 u_int8_t *src, *dst;
380
381#ifdef VBOX
382 s = tftp_session_allocate(pData, tp);
383#else /* !VBOX */
384 s = tftp_session_allocate(tp);
385#endif /* !VBOX */
386
387 if (s < 0) {
388 return;
389 }
390
391 spt = &tftp_sessions[s];
392
393 src = tp->x.tp_buf;
394 dst = spt->filename;
395 n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
396
397 /* get name */
398
399 for (k = 0; k < n; k++) {
400 if (k < TFTP_FILENAME_MAX) {
401 dst[k] = src[k];
402 }
403 else {
404 return;
405 }
406
407 if (src[k] == '\0') {
408 break;
409 }
410 }
411
412 if (k >= n) {
413 return;
414 }
415
416 k++;
417
418 /* check mode */
419 if ((n - k) < 6) {
420 return;
421 }
422
423 if (memcmp(&src[k], "octet\0", 6) != 0) {
424#ifdef VBOX
425 tftp_send_error(pData, spt, 4, "Unsupported transfer mode", tp);
426#else /* !VBOX */
427 tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
428#endif /* !VBOX */
429 return;
430 }
431
432 k += 6; /* skipping octet */
433
434 /* do sanity checks on the filename */
435
436#ifndef VBOX
437 if ((spt->filename[0] != '/')
438 || (spt->filename[strlen(spt->filename) - 1] == '/')
439 || strstr(spt->filename, "/../")) {
440 tftp_send_error(spt, 2, "Access violation", tp);
441#else /* VBOX */
442 if ((spt->filename[0] != '/')
443 || (spt->filename[strlen((const char *)spt->filename) - 1] == '/')
444 || strstr((char *)spt->filename, "/../")) {
445 tftp_send_error(pData, spt, 2, "Access violation", tp);
446#endif /* VBOX */
447 return;
448 }
449
450 /* only allow exported prefixes */
451
452 if (!tftp_prefix) {
453#ifdef VBOX
454 tftp_send_error(pData, spt, 2, "Access violation", tp);
455#else /* !VBOX */
456 tftp_send_error(spt, 2, "Access violation", tp);
457#endif /* !VBOX */
458 return;
459 }
460
461 /* check if the file exists */
462
463#ifdef VBOX
464 if (tftp_read_data(pData, spt, 0, spt->filename, 0) < 0) {
465 tftp_send_error(pData, spt, 1, "File not found", tp);
466#else /* !VBOX */
467 if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
468 tftp_send_error(spt, 1, "File not found", tp);
469#endif /* !VBOX */
470 return;
471 }
472
473 if (src[n - 1] != 0) {
474#ifdef VBOX
475 tftp_send_error(pData, spt, 2, "Access violation", tp);
476#else /* !VBOX */
477 tftp_send_error(spt, 2, "Access violation", tp);
478#endif /* !VBOX */
479 return;
480 }
481
482 while (k < n) {
483 const char *key, *value;
484
485#ifdef VBOX
486 key = (const char *)src + k;
487#else /* !VBOX */
488 key = src + k;
489#endif /* !VBOX */
490 k += strlen(key) + 1;
491
492 if (k >= n) {
493#ifdef VBOX
494 tftp_send_error(pData, spt, 2, "Access violation", tp);
495#else /* !VBOX */
496 tftp_send_error(spt, 2, "Access violation", tp);
497#endif /* !VBOX */
498 return;
499 }
500
501#ifdef VBOX
502 value = (const char *)src + k;
503#else /* !VBOX */
504 value = src + k;
505#endif /* !VBOX */
506 k += strlen(value) + 1;
507
508 if (strcmp(key, "tsize") == 0) {
509 int tsize = atoi(value);
510 struct stat stat_p;
511
512 if (tsize == 0 && tftp_prefix) {
513 char buffer[1024];
514 int len;
515
516#ifndef VBOX
517 len = snprintf(buffer, sizeof(buffer), "%s/%s",
518 tftp_prefix, spt->filename);
519#else
520 len = RTStrPrintf(buffer, sizeof(buffer), "%s/%s",
521 tftp_prefix, spt->filename);
522#endif
523
524 if (stat(buffer, &stat_p) == 0)
525 tsize = stat_p.st_size;
526 else {
527#ifdef VBOX
528 tftp_send_error(pData, spt, 1, "File not found", tp);
529#else /* !VBOX */
530 tftp_send_error(spt, 1, "File not found", tp);
531#endif /* !VBOX */
532 return;
533 }
534 }
535
536#ifdef VBOX
537 tftp_send_oack(pData, spt, "tsize", tsize, tp);
538#else /* !VBOX */
539 tftp_send_oack(spt, "tsize", tsize, tp);
540#endif /* !VBOX */
541 }
542 }
543
544#ifdef VBOX
545 tftp_send_data(pData, spt, 1, tp);
546#else /* !VBOX */
547 tftp_send_data(spt, 1, tp);
548#endif /* !VBOX */
549}
550
551#ifdef VBOX
552static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen)
553#else /* !VBOX */
554static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
555#endif /* !VBOX */
556{
557 int s;
558
559#ifdef VBOX
560 s = tftp_session_find(pData, tp);
561#else /* !VBOX */
562 s = tftp_session_find(tp);
563#endif /* !VBOX */
564
565 if (s < 0) {
566 return;
567 }
568
569#ifdef VBOX
570 if (tftp_send_data(pData, &tftp_sessions[s],
571 ntohs(tp->x.tp_data.tp_block_nr) + 1,
572 tp) < 0) {
573#else /* !VBOX */
574 if (tftp_send_data(&tftp_sessions[s],
575 ntohs(tp->x.tp_data.tp_block_nr) + 1,
576 tp) < 0) {
577#endif /* !VBOX */
578 return;
579 }
580}
581
582#ifdef VBOX
583void tftp_input(PNATState pData, struct mbuf *m)
584#else /* !VBOX */
585void tftp_input(struct mbuf *m)
586#endif /* !VBOX */
587{
588 struct tftp_t *tp = (struct tftp_t *)m->m_data;
589
590 switch(ntohs(tp->tp_op)) {
591 case TFTP_RRQ:
592#ifdef VBOX
593 tftp_handle_rrq(pData, tp, m->m_len);
594#else /* !VBOX */
595 tftp_handle_rrq(tp, m->m_len);
596#endif /* !VBOX */
597 break;
598
599 case TFTP_ACK:
600#ifdef VBOX
601 tftp_handle_ack(pData, tp, m->m_len);
602#else /* !VBOX */
603 tftp_handle_ack(tp, m->m_len);
604#endif /* !VBOX */
605 break;
606 }
607}
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