diff -urN --exclude=CVS qemu/Makefile.target qemu-20040716-tftp/Makefile.target --- qemu/Makefile.target 2004-07-14 19:19:55.000000000 +0200 +++ qemu-20040716-tftp/Makefile.target 2004-07-16 01:19:39.000000000 +0200 @@ -260,7 +260,7 @@ DEFINES+=-I$(SRC_PATH)/slirp SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \ slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \ -tcp_subr.o tcp_timer.o udp.o bootp.o debug.o +tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS)) endif diff -urN --exclude=CVS qemu/slirp/slirp.h qemu-20040716-tftp/slirp/slirp.h --- qemu/slirp/slirp.h 2004-07-13 00:33:05.000000000 +0200 +++ qemu-20040716-tftp/slirp/slirp.h 2004-07-16 01:02:29.000000000 +0200 @@ -210,6 +210,7 @@ #endif #include "bootp.h" +#include "tftp.h" #include "libslirp.h" extern struct ttys *ttys_unit[MAX_INTERFACES]; diff -urN --exclude=CVS qemu/slirp/tftp.c qemu-20040716-tftp/slirp/tftp.c --- qemu/slirp/tftp.c 1970-01-01 01:00:00.000000000 +0100 +++ qemu-20040716-tftp/slirp/tftp.c 2004-07-16 12:32:40.000000000 +0200 @@ -0,0 +1,343 @@ +/* + * tftp.c - a simple, read-only tftp server for qemu + * + * Copyright (c) 2004 Magnus Damm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +struct tftp_session { + int in_use; + unsigned char filename[TFTP_FILENAME_MAX]; + + struct in_addr client_ip; + u_int16_t client_port; + + struct timeval timestamp; +}; + +struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + +char *tftp_prefix; + +static void tftp_session_update(struct tftp_session *spt) +{ + gettimeofday(&spt->timestamp, 0); + spt->in_use = 1; +} + +static void tftp_session_terminate(struct tftp_session *spt) +{ + spt->in_use = 0; +} + +static int tftp_session_allocate(struct tftp_t *tp) +{ + struct tftp_session *spt; + struct timeval tv; + int k; + + gettimeofday(&tv, 0); + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (!spt->in_use) { + goto found; + } + + /* sessions time out after 5 inactive seconds */ + + if (tv.tv_sec > (spt->timestamp.tv_sec + 5)) { + goto found; + } + } + + return -1; + + found: + memset(spt, 0, sizeof(*spt)); + memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); + spt->client_port = tp->udp.uh_sport; + + tftp_session_update(spt); + + return k; +} + +static int tftp_session_find(struct tftp_t *tp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &tftp_sessions[k]; + + if (spt->in_use) { + if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) { + if (spt->client_port == tp->udp.uh_sport) { + return k; + } + } + } + } + + return -1; +} + +static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr, + u_int8_t *buf, int len) +{ + int fd; + int bytes_read = 0; + + fd = open(spt->filename, O_RDONLY); + + if (fd < 0) { + return -1; + } + + if (len) { + lseek(fd, block_nr * 512, SEEK_SET); + + bytes_read = read(fd, buf, len); + } + + close(fd); + + return bytes_read; +} + +static int tftp_send_error(struct tftp_session *spt, + u_int16_t errorcode, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct mbuf *m; + struct tftp_t *tp; + int nobytes; + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_ERROR); + tp->x.tp_data.tp_block_nr = htons(errorcode); + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + nobytes = 2; + + m->m_len = sizeof(struct tftp_t) - (512 - 2) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + tftp_session_terminate(spt); + + return 0; +} + +static int tftp_send_data(struct tftp_session *spt, + u_int16_t block_nr, + struct tftp_t *recv_tp) +{ + struct sockaddr_in saddr, daddr; + struct mbuf *m; + struct tftp_t *tp; + int nobytes; + + if (block_nr < 1) { + return -1; + } + + m = m_get(); + + if (!m) { + return -1; + } + + memset(m->m_data, 0, m->m_size); + + m->m_data += if_maxlinkhdr; + tp = (void *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + + tp->tp_op = htons(TFTP_DATA); + tp->x.tp_data.tp_block_nr = htons(block_nr); + + saddr.sin_addr = recv_tp->ip.ip_dst; + saddr.sin_port = recv_tp->udp.uh_dport; + + daddr.sin_addr = spt->client_ip; + daddr.sin_port = spt->client_port; + + nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512); + + if (nobytes < 0) { + m_free(m); + + /* send "file not found" error back */ + + tftp_send_error(spt, 1, tp); + + return -1; + } + + m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - + sizeof(struct ip) - sizeof(struct udphdr); + + udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + + if (nobytes == 512) { + tftp_session_update(spt); + } + else { + tftp_session_terminate(spt); + } + + return 0; +} + +static void tftp_handle_rrq(struct tftp_t *tp, int pktlen) +{ + struct tftp_session *spt; + int s, k, n; + u_int8_t *src, *dst; + + s = tftp_session_allocate(tp); + + if (s < 0) { + return; + } + + spt = &tftp_sessions[s]; + + src = tp->x.tp_buf; + dst = spt->filename; + n = pktlen - ((void *)&tp->x.tp_buf[0] - (void *)tp); + + /* get name */ + + for (k = 0; k < n; k++) { + if (k < TFTP_FILENAME_MAX) { + dst[k] = src[k]; + } + else { + return; + } + + if (src[k] == '\0') { + break; + } + } + + if (k >= n) { + return; + } + + k++; + + /* check mode */ + + if ((n - k) < 6) { + return; + } + + if (memcmp(&src[k], "octet\0", 6) != 0) { + return; + } + + /* do sanity checks on the filename */ + + if ((spt->filename[0] != '/') + || (spt->filename[strlen(spt->filename - 1)] == '/') + || strstr(spt->filename, "/../")) { + n = 2; /* access violation */ + goto error_n; + } + + /* only allow exported prefixes */ + + if (!tftp_prefix + || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) { + n = 2; /* access violation */ + goto error_n; + } + + /* check if the file exists */ + + if (tftp_read_data(spt, 0, spt->filename, 0) < 0) { + n = 1; /* file not found */ + goto error_n; + } + + tftp_send_data(spt, 1, tp); + + return; + + error_n: + tftp_send_error(&tftp_sessions[s], n, tp); +} + +static void tftp_handle_ack(struct tftp_t *tp, int pktlen) +{ + int s; + + s = tftp_session_find(tp); + + if (s < 0) { + return; + } + + if (tftp_send_data(&tftp_sessions[s], + ntohs(tp->x.tp_data.tp_block_nr) + 1, + tp) < 0) { + return; + } +} + +void tftp_input(struct mbuf *m) +{ + struct tftp_t *tp = (struct tftp_t *)m->m_data; + + switch(ntohs(tp->tp_op)) { + case TFTP_RRQ: + tftp_handle_rrq(tp, m->m_len); + break; + + case TFTP_ACK: + tftp_handle_ack(tp, m->m_len); + break; + } +} diff -urN --exclude=CVS qemu/slirp/tftp.h qemu-20040716-tftp/slirp/tftp.h --- qemu/slirp/tftp.h 1970-01-01 01:00:00.000000000 +0100 +++ qemu-20040716-tftp/slirp/tftp.h 2004-07-16 12:09:44.000000000 +0200 @@ -0,0 +1,30 @@ +/* tftp defines */ + +#define TFTP_SESSIONS_MAX 3 + +#define TFTP_SERVER 69 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +#define TFTP_FILENAME_MAX 512 + +struct tftp_t { + struct ip ip; + struct udphdr udp; + u_int16_t tp_op; + union { + struct { + u_int16_t tp_block_nr; + u_int8_t tp_buf[512]; + } tp_data; + u_int8_t tp_buf[512 + 2]; + } x; +}; + +extern char *tftp_prefix; + +void tftp_input(struct mbuf *m); diff -urN --exclude=CVS qemu/slirp/udp.c qemu-20040716-tftp/slirp/udp.c --- qemu/slirp/udp.c 2004-07-13 00:33:07.000000000 +0200 +++ qemu-20040716-tftp/slirp/udp.c 2004-07-16 01:18:51.000000000 +0200 @@ -153,6 +153,14 @@ goto bad; } + /* + * handle TFTP + */ + if (ntohs(uh->uh_dport) == TFTP_SERVER) { + tftp_input(m); + goto bad; + } + /* * Locate pcb for datagram. */ diff -urN --exclude=CVS qemu/vl.c qemu-20040716-tftp/vl.c --- qemu/vl.c 2004-07-14 19:27:31.000000000 +0200 +++ qemu-20040716-tftp/vl.c 2004-07-16 12:20:33.000000000 +0200 @@ -2344,6 +2344,7 @@ "-tun-fd fd use this fd as already opened tap/tun interface\n" #ifdef CONFIG_SLIRP "-user-net use user mode network stack [default if no tap/tun script]\n" + "-tftp prefix allow tftp access to files starting with prefix [only with -user-net enabled]\n" #endif "-dummy-net use dummy network stack\n" "\n" @@ -2418,6 +2419,7 @@ QEMU_OPTION_n, QEMU_OPTION_tun_fd, QEMU_OPTION_user_net, + QEMU_OPTION_tftp, QEMU_OPTION_dummy_net, QEMU_OPTION_kernel, @@ -2470,6 +2472,7 @@ { "tun-fd", HAS_ARG, QEMU_OPTION_tun_fd }, #ifdef CONFIG_SLIRP { "user-net", 0, QEMU_OPTION_user_net }, + { "tftp", HAS_ARG, QEMU_OPTION_tftp }, #endif { "dummy-net", 0, QEMU_OPTION_dummy_net }, @@ -2719,9 +2722,17 @@ } } break; +#ifdef CONFIG_SLIRP + case QEMU_OPTION_tftp: + { + extern const char *tftp_prefix; + tftp_prefix = optarg; + } + break; case QEMU_OPTION_user_net: net_if_type = NET_IF_USER; break; +#endif case QEMU_OPTION_dummy_net: net_if_type = NET_IF_DUMMY; break;