|
|
1.1 root 1: /*
2: * tftp.c - a simple, read-only tftp server for qemu
1.1.1.2 ! root 3: *
1.1 root 4: * Copyright (c) 2004 Magnus Damm <[email protected]>
1.1.1.2 ! root 5: *
1.1 root 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: struct tftp_session {
28: int in_use;
29: unsigned char filename[TFTP_FILENAME_MAX];
1.1.1.2 ! root 30:
1.1 root 31: struct in_addr client_ip;
32: u_int16_t client_port;
1.1.1.2 ! root 33:
1.1 root 34: int timestamp;
35: };
36:
1.1.1.2 ! root 37: static struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
1.1 root 38:
39: const char *tftp_prefix;
40:
41: static void tftp_session_update(struct tftp_session *spt)
42: {
43: spt->timestamp = curtime;
44: spt->in_use = 1;
45: }
46:
47: static void tftp_session_terminate(struct tftp_session *spt)
48: {
49: spt->in_use = 0;
50: }
51:
52: static int tftp_session_allocate(struct tftp_t *tp)
53: {
54: struct tftp_session *spt;
55: int k;
56:
57: for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
58: spt = &tftp_sessions[k];
59:
60: if (!spt->in_use)
61: goto found;
62:
63: /* sessions time out after 5 inactive seconds */
64: if ((int)(curtime - spt->timestamp) > 5000)
65: goto found;
66: }
67:
68: return -1;
69:
70: found:
71: memset(spt, 0, sizeof(*spt));
72: memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
73: spt->client_port = tp->udp.uh_sport;
74:
75: tftp_session_update(spt);
76:
77: return k;
78: }
79:
80: static int tftp_session_find(struct tftp_t *tp)
81: {
82: struct tftp_session *spt;
83: int k;
84:
85: for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
86: spt = &tftp_sessions[k];
87:
88: if (spt->in_use) {
89: if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
90: if (spt->client_port == tp->udp.uh_sport) {
91: return k;
92: }
93: }
94: }
95: }
96:
97: return -1;
98: }
99:
100: static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
101: u_int8_t *buf, int len)
102: {
103: int fd;
104: int bytes_read = 0;
1.1.1.2 ! root 105: char buffer[1024];
! 106: int n;
! 107:
! 108: n = snprintf(buffer, sizeof(buffer), "%s/%s",
! 109: tftp_prefix, spt->filename);
! 110: if (n >= sizeof(buffer))
! 111: return -1;
1.1 root 112:
1.1.1.2 ! root 113: fd = open(buffer, O_RDONLY | O_BINARY);
1.1 root 114:
115: if (fd < 0) {
116: return -1;
117: }
118:
119: if (len) {
120: lseek(fd, block_nr * 512, SEEK_SET);
121:
122: bytes_read = read(fd, buf, len);
123: }
124:
125: close(fd);
126:
127: return bytes_read;
128: }
129:
1.1.1.2 ! root 130: static int tftp_send_oack(struct tftp_session *spt,
! 131: const char *key, uint32_t value,
! 132: struct tftp_t *recv_tp)
! 133: {
! 134: struct sockaddr_in saddr, daddr;
! 135: struct mbuf *m;
! 136: struct tftp_t *tp;
! 137: int n = 0;
! 138:
! 139: m = m_get();
! 140:
! 141: if (!m)
! 142: return -1;
! 143:
! 144: memset(m->m_data, 0, m->m_size);
! 145:
! 146: m->m_data += IF_MAXLINKHDR;
! 147: tp = (void *)m->m_data;
! 148: m->m_data += sizeof(struct udpiphdr);
! 149:
! 150: tp->tp_op = htons(TFTP_OACK);
! 151: n += sprintf(tp->x.tp_buf + n, "%s", key) + 1;
! 152: n += sprintf(tp->x.tp_buf + n, "%u", value) + 1;
! 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(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
! 163:
! 164: return 0;
! 165: }
! 166:
! 167:
! 168:
! 169: static int tftp_send_error(struct tftp_session *spt,
1.1 root 170: u_int16_t errorcode, const char *msg,
171: struct tftp_t *recv_tp)
172: {
173: struct sockaddr_in saddr, daddr;
174: struct mbuf *m;
175: struct tftp_t *tp;
176: int nobytes;
177:
178: m = m_get();
179:
180: if (!m) {
181: return -1;
182: }
183:
184: memset(m->m_data, 0, m->m_size);
185:
1.1.1.2 ! root 186: m->m_data += IF_MAXLINKHDR;
1.1 root 187: tp = (void *)m->m_data;
188: m->m_data += sizeof(struct udpiphdr);
1.1.1.2 ! root 189:
1.1 root 190: tp->tp_op = htons(TFTP_ERROR);
191: tp->x.tp_error.tp_error_code = htons(errorcode);
192: strcpy(tp->x.tp_error.tp_msg, msg);
193:
194: saddr.sin_addr = recv_tp->ip.ip_dst;
195: saddr.sin_port = recv_tp->udp.uh_dport;
196:
197: daddr.sin_addr = spt->client_ip;
198: daddr.sin_port = spt->client_port;
199:
200: nobytes = 2;
201:
1.1.1.2 ! root 202: m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
1.1 root 203: sizeof(struct ip) - sizeof(struct udphdr);
204:
205: udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
206:
207: tftp_session_terminate(spt);
208:
209: return 0;
210: }
211:
1.1.1.2 ! root 212: static int tftp_send_data(struct tftp_session *spt,
1.1 root 213: u_int16_t block_nr,
214: struct tftp_t *recv_tp)
215: {
216: struct sockaddr_in saddr, daddr;
217: struct mbuf *m;
218: struct tftp_t *tp;
219: int nobytes;
220:
221: if (block_nr < 1) {
222: return -1;
223: }
224:
225: m = m_get();
226:
227: if (!m) {
228: return -1;
229: }
230:
231: memset(m->m_data, 0, m->m_size);
232:
1.1.1.2 ! root 233: m->m_data += IF_MAXLINKHDR;
1.1 root 234: tp = (void *)m->m_data;
235: m->m_data += sizeof(struct udpiphdr);
1.1.1.2 ! root 236:
1.1 root 237: tp->tp_op = htons(TFTP_DATA);
238: tp->x.tp_data.tp_block_nr = htons(block_nr);
239:
240: saddr.sin_addr = recv_tp->ip.ip_dst;
241: saddr.sin_port = recv_tp->udp.uh_dport;
242:
243: daddr.sin_addr = spt->client_ip;
244: daddr.sin_port = spt->client_port;
245:
246: nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
247:
248: if (nobytes < 0) {
249: m_free(m);
250:
251: /* send "file not found" error back */
252:
253: tftp_send_error(spt, 1, "File not found", tp);
254:
255: return -1;
256: }
257:
1.1.1.2 ! root 258: m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
1.1 root 259: sizeof(struct ip) - sizeof(struct udphdr);
260:
261: udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
262:
263: if (nobytes == 512) {
264: tftp_session_update(spt);
265: }
266: else {
267: tftp_session_terminate(spt);
268: }
269:
270: return 0;
271: }
272:
273: static void tftp_handle_rrq(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(tp);
280:
281: if (s < 0) {
282: return;
283: }
284:
285: spt = &tftp_sessions[s];
286:
287: src = tp->x.tp_buf;
288: dst = spt->filename;
289: n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
290:
291: /* get name */
292:
293: for (k = 0; k < n; k++) {
294: if (k < TFTP_FILENAME_MAX) {
295: dst[k] = src[k];
296: }
297: else {
298: return;
299: }
1.1.1.2 ! root 300:
1.1 root 301: if (src[k] == '\0') {
302: break;
303: }
304: }
1.1.1.2 ! root 305:
1.1 root 306: if (k >= n) {
307: return;
308: }
1.1.1.2 ! root 309:
1.1 root 310: k++;
1.1.1.2 ! root 311:
1.1 root 312: /* check mode */
313: if ((n - k) < 6) {
314: return;
315: }
1.1.1.2 ! root 316:
1.1 root 317: if (memcmp(&src[k], "octet\0", 6) != 0) {
318: tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
319: return;
320: }
321:
1.1.1.2 ! root 322: k += 6; /* skipping octet */
! 323:
1.1 root 324: /* do sanity checks on the filename */
325:
326: if ((spt->filename[0] != '/')
327: || (spt->filename[strlen(spt->filename) - 1] == '/')
328: || strstr(spt->filename, "/../")) {
329: tftp_send_error(spt, 2, "Access violation", tp);
330: return;
331: }
332:
333: /* only allow exported prefixes */
334:
1.1.1.2 ! root 335: if (!tftp_prefix) {
1.1 root 336: tftp_send_error(spt, 2, "Access violation", tp);
337: return;
338: }
339:
340: /* check if the file exists */
1.1.1.2 ! root 341:
1.1 root 342: if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
343: tftp_send_error(spt, 1, "File not found", tp);
344: return;
345: }
346:
1.1.1.2 ! root 347: if (src[n - 1] != 0) {
! 348: tftp_send_error(spt, 2, "Access violation", tp);
! 349: return;
! 350: }
! 351:
! 352: while (k < n) {
! 353: const char *key, *value;
! 354:
! 355: key = src + k;
! 356: k += strlen(key) + 1;
! 357:
! 358: if (k >= n) {
! 359: tftp_send_error(spt, 2, "Access violation", tp);
! 360: return;
! 361: }
! 362:
! 363: value = src + k;
! 364: k += strlen(value) + 1;
! 365:
! 366: if (strcmp(key, "tsize") == 0) {
! 367: int tsize = atoi(value);
! 368: struct stat stat_p;
! 369:
! 370: if (tsize == 0 && tftp_prefix) {
! 371: char buffer[1024];
! 372: int len;
! 373:
! 374: len = snprintf(buffer, sizeof(buffer), "%s/%s",
! 375: tftp_prefix, spt->filename);
! 376:
! 377: if (stat(buffer, &stat_p) == 0)
! 378: tsize = stat_p.st_size;
! 379: else {
! 380: tftp_send_error(spt, 1, "File not found", tp);
! 381: return;
! 382: }
! 383: }
! 384:
! 385: tftp_send_oack(spt, "tsize", tsize, tp);
! 386: }
! 387: }
! 388:
1.1 root 389: tftp_send_data(spt, 1, tp);
390: }
391:
392: static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
393: {
394: int s;
395:
396: s = tftp_session_find(tp);
397:
398: if (s < 0) {
399: return;
400: }
401:
1.1.1.2 ! root 402: if (tftp_send_data(&tftp_sessions[s],
! 403: ntohs(tp->x.tp_data.tp_block_nr) + 1,
1.1 root 404: tp) < 0) {
405: return;
406: }
407: }
408:
409: void tftp_input(struct mbuf *m)
410: {
411: struct tftp_t *tp = (struct tftp_t *)m->m_data;
412:
413: switch(ntohs(tp->tp_op)) {
414: case TFTP_RRQ:
415: tftp_handle_rrq(tp, m->m_len);
416: break;
417:
418: case TFTP_ACK:
419: tftp_handle_ack(tp, m->m_len);
420: break;
421: }
422: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.