qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Magnus Damm <damm@opensource.se>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] tftp for slirp
Date: Fri, 16 Jul 2004 13:18:48 +0200	[thread overview]
Message-ID: <1089976728.27010.76.camel@kubu.opensource.se> (raw)

[-- Attachment #1: Type: text/plain, Size: 555 bytes --]

Hi again,

The attached patch adds a simple read-only tftp server to the slirp
code. The code is no beauty contest winner, but it works for me(tm).
The server is tested against the tftp client shipped with busybox.

Use the command line switch -tftp to add a prefix that should be
exported by the server. Using -tftp "/home/d" will export both
"/home/damm" and "/home/douglas". The server exports nothing by default.

Cool things to add in the future would be blocksize support and options
passing from the bootp/dhcp client...

/ magnus - not subscribed

[-- Attachment #2: qemu-20040716-tftp.patch --]
[-- Type: text/x-patch, Size: 10943 bytes --]

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 <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 --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;

             reply	other threads:[~2004-07-16 11:15 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-07-16 11:18 Magnus Damm [this message]
2004-07-16 16:33 ` [Qemu-devel] [PATCH] tftp for slirp John R. Hogerhuis

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1089976728.27010.76.camel@kubu.opensource.se \
    --to=damm@opensource.se \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).