* [Qemu-devel] [PATCH] tftp server, second try
@ 2004-08-15 18:52 Magnus Damm
0 siblings, 0 replies; only message in thread
From: Magnus Damm @ 2004-08-15 18:52 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1069 bytes --]
Hi again,
Attached is a patch that adds a tftp server to the slirp networking
code, ie the patch makes it possible for the guest to read files from
the host if the user network implementation enabled with -user-net.
This is the second try, the first patch contained a fatal bug that did
not show on my PowerPC box (misplaced parenthesis, aargh) - I recently
tried the code on a x86 box and realized that all read requests were
treated as access violations... Sorry about that. Please try this patch
instead, it has been tested on CVS today and 0.60.0 under Linux/PPC and
0.60.0 under Linux/x86. This patch applies both to CVS and 0.60.0.
The option -tftp is used to tell the tftp server which files that should
be exported as read only data. Default is no exports, "-tftp /" will
make all files on your host available to your guest. The tftp server
only accepts absolute filenames, no relative paths are allowed.
If someone wants to test the code I would be happy to hand over a
precompiled x86 image containing a tftp client useful for testing.
Thanks.
/ magnus
[-- Attachment #2: qemu-0.6.0-tftp_20040815.patch --]
[-- Type: text/x-patch, Size: 10895 bytes --]
diff -urN qemu-0.6.0/Makefile.target qemu-0.6.0-tftp/Makefile.target
--- qemu-0.6.0/Makefile.target 2004-07-10 20:20:09.000000000 +0200
+++ qemu-0.6.0-tftp/Makefile.target 2004-08-09 20:09:08.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 qemu-0.6.0/slirp/slirp.h qemu-0.6.0-tftp/slirp/slirp.h
--- qemu-0.6.0/slirp/slirp.h 2004-07-10 20:20:09.000000000 +0200
+++ qemu-0.6.0-tftp/slirp/slirp.h 2004-08-09 20:09:08.000000000 +0200
@@ -195,6 +195,7 @@
#endif
#include "bootp.h"
+#include "tftp.h"
#include "libslirp.h"
extern struct ttys *ttys_unit[MAX_INTERFACES];
diff -urN qemu-0.6.0/slirp/tftp.c qemu-0.6.0-tftp/slirp/tftp.c
--- qemu-0.6.0/slirp/tftp.c 1970-01-01 01:00:00.000000000 +0100
+++ qemu-0.6.0-tftp/slirp/tftp.c 2004-08-09 20:50:28.000000000 +0200
@@ -0,0 +1,343 @@
+/*
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * 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 <slirp.h>
+
+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 qemu-0.6.0/slirp/tftp.h qemu-0.6.0-tftp/slirp/tftp.h
--- qemu-0.6.0/slirp/tftp.h 1970-01-01 01:00:00.000000000 +0100
+++ qemu-0.6.0-tftp/slirp/tftp.h 2004-08-09 20:09:08.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 qemu-0.6.0/slirp/udp.c qemu-0.6.0-tftp/slirp/udp.c
--- qemu-0.6.0/slirp/udp.c 2004-07-10 20:20:09.000000000 +0200
+++ qemu-0.6.0-tftp/slirp/udp.c 2004-08-09 20:09:08.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 qemu-0.6.0/vl.c qemu-0.6.0-tftp/vl.c
--- qemu-0.6.0/vl.c 2004-07-10 20:20:09.000000000 +0200
+++ qemu-0.6.0-tftp/vl.c 2004-08-09 20:09:08.000000000 +0200
@@ -2097,6 +2097,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"
@@ -2164,6 +2165,7 @@
QEMU_OPTION_n,
QEMU_OPTION_tun_fd,
QEMU_OPTION_user_net,
+ QEMU_OPTION_tftp,
QEMU_OPTION_dummy_net,
QEMU_OPTION_kernel,
@@ -2214,6 +2216,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 },
@@ -2454,9 +2457,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;
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2004-08-15 18:50 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-08-15 18:52 [Qemu-devel] [PATCH] tftp server, second try Magnus Damm
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).