port redirection support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1054 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/Changelog b/Changelog
index 4b6f315..430ef55 100644
--- a/Changelog
+++ b/Changelog
@@ -9,6 +9,7 @@
   - VMware 3 and 4 read-only disk image support (untested)
   - Support for up to 4 serial ports
   - TFTP server support (Magnus Damm)
+  - Port redirection support in user mode networking
 
 version 0.6.0:
 
diff --git a/qemu-doc.texi b/qemu-doc.texi
index ba3ade8..7493d51 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -228,6 +228,44 @@
 Use the user mode network stack. This is the default if no tun/tap
 network init script is found.
 
+@item -tftp prefix
+When using the user mode network stack, activate a built-in TFTP
+server. All filenames beginning with @var{prefix} can be downloaded
+from the host to the guest using a TFTP client. The TFTP client on the
+guest must be configured in binary mode (use the command @code{bin} of
+the Unix TFTP client). The host IP address on the guest is as usual
+10.0.2.2.
+
+@item -redir [tcp|udp]:host-port:[guest-host]:guest-port
+
+When using the user mode network stack, redirect incoming TCP or UDP
+connections to the host port @var{host-port} to the guest
+@var{guest-host} on guest port @var{guest-port}. If @var{guest-host}
+is not specified, its value is 10.0.2.15 (default address given by the
+built-in DHCP server).
+
+For example, to redirect host X11 connection from screen 1 to guest
+screen 0, use the following:
+
+@example
+# on the host
+qemu -redir tcp:6001::6000 [...]
+# this host xterm should open in the guest X11 server
+xterm -display :1
+@end example
+
+To redirect telnet connections from host port 5555 to telnet port on
+the guest, use the following:
+
+@example
+# on the host
+qemu -redir tcp:5555::23 [...]
+telnet localhost 5555
+@end example
+
+Then when you use on the host @code{telnet localhost 5555}, you
+connect to the guest telnet server.
+
 @item -dummy-net 
 Use the dummy network stack: no packet will be received by the network
 cards.
@@ -652,7 +690,12 @@
 would require root priviledges. It means you can only ping the local
 router (10.0.2.2).
 
-The user mode network is currently only supported on a Unix host.
+When using the built-in TFTP server, the router is also the TFTP
+server.
+
+When using the @option{-redir} option, TCP or UDP connections can be
+redirected from the host to the guest. It allows for example to
+redirect X11, telnet or SSH connections.
 
 @node direct_linux_boot
 @section Direct Linux Boot
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 47824b2..772427d 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -3,8 +3,10 @@
 
 #ifdef _WIN32
 #include <winsock2.h>
+int inet_aton(const char *cp, struct in_addr *ia);
 #else
 #include <sys/select.h>
+#include <arpa/inet.h>
 #endif
 
 void slirp_init(void);
@@ -20,4 +22,9 @@
 int slirp_can_output(void);
 void slirp_output(const uint8_t *pkt, int pkt_len);
 
+int slirp_redir(int is_udp, int host_port, 
+                struct in_addr guest_addr, int guest_port);
+
+extern const char *tftp_prefix;
+
 #endif
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 405647b..bc2b155 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -617,3 +617,18 @@
     memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
     slirp_output(buf, ip_data_len + ETH_HLEN);
 }
+
+int slirp_redir(int is_udp, int host_port, 
+                struct in_addr guest_addr, int guest_port)
+{
+    if (is_udp) {
+        if (!udp_listen(htons(host_port), guest_addr.s_addr, 
+                        htons(guest_port), 0))
+            return -1;
+    } else {
+        if (!solisten(htons(host_port), guest_addr.s_addr, 
+                      htons(guest_port), 0))
+            return -1;
+    }
+    return 0;
+}
diff --git a/slirp/tftp.c b/slirp/tftp.c
index e50d255..9052662 100644
--- a/slirp/tftp.c
+++ b/slirp/tftp.c
@@ -36,7 +36,7 @@
 
 struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
 
-char *tftp_prefix;
+const char *tftp_prefix;
 
 static void tftp_session_update(struct tftp_session *spt)
 {
diff --git a/slirp/tftp.h b/slirp/tftp.h
index 3ee666a..f0560b6 100644
--- a/slirp/tftp.h
+++ b/slirp/tftp.h
@@ -29,6 +29,4 @@
   } x;
 };
 
-extern char *tftp_prefix;
-
 void tftp_input(struct mbuf *m);
diff --git a/vl.c b/vl.c
index f05d73a..5ba5b97 100644
--- a/vl.c
+++ b/vl.c
@@ -1382,6 +1382,78 @@
     return 0;
 }
 
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+    const char *p, *p1;
+    int len;
+    p = *pp;
+    p1 = strchr(p, sep);
+    if (!p1)
+        return -1;
+    len = p1 - p;
+    p1++;
+    if (buf_size > 0) {
+        if (len > buf_size - 1)
+            len = buf_size - 1;
+        memcpy(buf, p, len);
+        buf[len] = '\0';
+    }
+    *pp = p1;
+    return 0;
+}
+
+static void net_slirp_redir(const char *redir_str)
+{
+    int is_udp;
+    char buf[256], *r;
+    const char *p;
+    struct in_addr guest_addr;
+    int host_port, guest_port;
+    
+    if (!slirp_inited) {
+        slirp_inited = 1;
+        slirp_init();
+    }
+
+    p = redir_str;
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    if (!strcmp(buf, "tcp")) {
+        is_udp = 0;
+    } else if (!strcmp(buf, "udp")) {
+        is_udp = 1;
+    } else {
+        goto fail;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    host_port = strtol(buf, &r, 0);
+    if (r == buf)
+        goto fail;
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    if (buf[0] == '\0') {
+        pstrcpy(buf, sizeof(buf), "10.0.2.15");
+    }
+    if (!inet_aton(buf, &guest_addr))
+        goto fail;
+    
+    guest_port = strtol(p, &r, 0);
+    if (r == p)
+        goto fail;
+    
+    if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) {
+        fprintf(stderr, "qemu: could not set up redirection\n");
+        exit(1);
+    }
+    return;
+ fail:
+    fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n");
+    exit(1);
+}
+
 #endif /* CONFIG_SLIRP */
 
 #if !defined(_WIN32)
@@ -2334,7 +2406,9 @@
            "-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"
+           "-tftp prefix    allow tftp access to files starting with prefix [-user-net]\n"
+           "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
+           "                redirect TCP or UDP connections from host to guest [-user-net]\n"
 #endif
            "-dummy-net      use dummy network stack\n"
            "\n"
@@ -2410,6 +2484,7 @@
     QEMU_OPTION_tun_fd,
     QEMU_OPTION_user_net,
     QEMU_OPTION_tftp,
+    QEMU_OPTION_redir,
     QEMU_OPTION_dummy_net,
 
     QEMU_OPTION_kernel,
@@ -2463,6 +2538,7 @@
 #ifdef CONFIG_SLIRP
     { "user-net", 0, QEMU_OPTION_user_net },
     { "tftp", HAS_ARG, QEMU_OPTION_tftp },
+    { "redir", HAS_ARG, QEMU_OPTION_redir },
 #endif
     { "dummy-net", 0, QEMU_OPTION_dummy_net },
 
@@ -2756,14 +2832,14 @@
                 break;
 #ifdef CONFIG_SLIRP
             case QEMU_OPTION_tftp:
-	      {
-		extern const char *tftp_prefix;
 		tftp_prefix = optarg;
-	      }
-	      break;
+                break;
             case QEMU_OPTION_user_net:
                 net_if_type = NET_IF_USER;
                 break;
+            case QEMU_OPTION_redir:
+                net_slirp_redir(optarg);                
+                break;
 #endif
             case QEMU_OPTION_dummy_net:
                 net_if_type = NET_IF_DUMMY;