qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] telnet IAC options and simplified tcp & udp options


From: Jason Wessel
Subject: [Qemu-devel] [PATCH] telnet IAC options and simplified tcp & udp options
Date: Tue, 27 Jun 2006 09:53:16 -0500
User-agent: Thunderbird 1.5.0.4 (Windows/20060516)

This patch simplifies the syntax of the tcp and udp options, as well as adding the telnet protocol.

signed-off-by: address@hidden

Jason.
Index: qemu/vl.c
===================================================================
--- qemu.orig/vl.c
+++ qemu/vl.c
@@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(Cha
 }
 
 int parse_host_port(struct sockaddr_in *saddr, const char *str);
+int parse_host_src_port(struct sockaddr_in *haddr,
+                        struct sockaddr_in *saddr,
+                        const char *str);
 
 CharDriverState *qemu_chr_open_udp(const char *def)
 {
     CharDriverState *chr = NULL;
     NetCharDriver *s = NULL;
     int fd = -1;
-    int con_type;
-    struct sockaddr_in addr;
-    const char *p, *r;
-    int port;
+    struct sockaddr_in saddr;
 
     chr = qemu_mallocz(sizeof(CharDriverState));
     if (!chr)
@@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const
         goto return_err;
     }
 
-    /* There are three types of port definitions
-     * 1) udp:remote_port
-     *    Juse use 0.0.0.0 for the IP and send to remote
-     * 2) udp:remote_host:port
-     *    Use a IP and send traffic to remote
-     * 3) udp:local_port:remote_host:remote_port
-     *    Use local_port as the originator + #2
-     */
-    con_type = 0;
-    p = def;
-    while ((p = strchr(p, ':'))) {
-        p++;
-        con_type++;
-    }
-
-    p = def;
-    memset(&addr,0,sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    s->daddr.sin_family = AF_INET;
-    s->daddr.sin_addr.s_addr = htonl(INADDR_ANY);
-
-    switch (con_type) {
-        case 0:
-            port = strtol(p, (char **)&r, 0);
-            if (r == p) {
-                fprintf(stderr, "Error parsing port number\n");
-                goto return_err;
-            }
-            s->daddr.sin_port = htons((short)port);
-            break;
-        case 2:
-            port = strtol(p, (char **)&r, 0);
-            if (r == p) {
-                fprintf(stderr, "Error parsing port number\n");
-                goto return_err;
-            }
-            addr.sin_port = htons((short)port);
-            p = r + 1;
-            /* Fall through to case 1 now that we have the local port */
-        case 1:
-            if (parse_host_port(&s->daddr, p) < 0) {
-                fprintf(stderr, "Error parsing host name and port\n");
-                goto return_err;
-            }
-            break;
-        default:
-            fprintf(stderr, "Too many ':' characters\n");
-            goto return_err;
+    if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
+        printf("Could not parse: %s\n", def);
+        goto return_err;
     }
 
-    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+    if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
     {
         perror("bind");
         goto return_err;
@@ -2312,6 +2266,7 @@ typedef struct {
     int fd, listen_fd;
     int connected;
     int max_size;
+    int do_telnetopt;
 } TCPCharDriver;
 
 static void tcp_chr_accept(void *opaque);
@@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaqu
     return s->max_size;
 }
 
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+                                      TCPCharDriver *s,
+                                      char *buf, int *size)
+{
+    /* Handle any telnet client's basic IAC options to satisfy char by
+     * char mode with no echo.  All IAC options will be removed from
+     * the buf and the do_telnetopt variable will be used to track the
+     * state of the width of the IAC information.
+     *
+     * IAC commands come in sets of 3 bytes with the exception of the
+     * "IAC BREAK" command and the double IAC.
+     */
+
+    int i;
+    int j = 0;
+
+    for (i = 0; i < *size; i++) {
+        if (s->do_telnetopt > 1) {
+            if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+                /* Double IAC means send an IAC */
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+                s->do_telnetopt = 1;
+            } else {
+                if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 
2) {
+                    /* Handle IAC break commands by sending a serial break */
+                    chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK);
+                    s->do_telnetopt++;
+                }
+                s->do_telnetopt++;
+            }
+            if (s->do_telnetopt >= 4) {
+                s->do_telnetopt = 1;
+            }
+        } else {
+            if ((unsigned char)buf[i] == IAC) {
+                s->do_telnetopt = 2;
+            } else {
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+            }
+        }
+    }
+    *size = j;
+}
+
 static void tcp_chr_read(void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque)
         closesocket(s->fd);
         s->fd = -1;
     } else if (size > 0) {
-        s->fd_read(s->fd_opaque, buf, size);
+        if (s->do_telnetopt)
+            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+        if (size > 0)
+            s->fd_read(s->fd_opaque, buf, size);
     }
 }
 
@@ -2385,6 +2393,21 @@ static void tcp_chr_connect(void *opaque
                          tcp_chr_read, NULL, chr);
 }
 
+#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+static void tcp_chr_telnet_init(int fd)
+{
+    char buf[3];
+    /* Send the telnet negotion to put telnet in binary, no echo, single char 
mode */
+    IACSET(buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
+    send(fd, (char *)buf, 3, 0);
+}
+
 static void tcp_chr_accept(void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque)
         if (fd < 0 && errno != EINTR) {
             return;
         } else if (fd >= 0) {
+            if (s->do_telnetopt)
+                tcp_chr_telnet_init(fd);
             break;
         }
     }
@@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverStat
 }
 
 static CharDriverState *qemu_chr_open_tcp(const char *host_str, 
-                                          int is_listen)
+                                          int is_telnet)
 {
     CharDriverState *chr = NULL;
     TCPCharDriver *s = NULL;
     int fd = -1, ret, err, val;
+    int is_listen = 0;
+    int is_waitconnect = 1;
+    const char *ptr;
     struct sockaddr_in saddr;
 
     if (parse_host_port(&saddr, host_str) < 0)
         goto fail;
 
+    ptr = host_str;
+    while((ptr = strchr(ptr,','))) {
+        ptr++;
+        if (!strncmp(ptr,"server",6)) {
+            is_listen = 1;
+        } else if (!strncmp(ptr,"nowait",6)) {
+            is_waitconnect = 0;
+        } else {
+            printf("Unknown option: %s\n", ptr);
+            goto fail;
+        }
+    }
+    if (!is_listen)
+        is_waitconnect = 0;
+
     chr = qemu_mallocz(sizeof(CharDriverState));
     if (!chr)
         goto fail;
@@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tc
     fd = socket(PF_INET, SOCK_STREAM, 0);
     if (fd < 0) 
         goto fail;
-    socket_set_nonblock(fd);
+
+    if (!is_waitconnect)
+        socket_set_nonblock(fd);
 
     s->connected = 0;
     s->fd = -1;
@@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tc
             goto fail;
         s->listen_fd = fd;
         qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+        if (is_telnet)
+            s->do_telnetopt = 1;
     } else {
         for(;;) {
             ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
@@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tc
     chr->chr_write = tcp_chr_write;
     chr->chr_add_read_handler = tcp_chr_add_read_handler;
     chr->chr_close = tcp_chr_close;
+    if (is_listen && is_waitconnect) {
+        printf("QEMU waiting for connection on: %s\n", host_str);
+        tcp_chr_accept(chr);
+        socket_set_nonblock(s->listen_fd);
+    }
+
     return chr;
  fail:
     if (fd >= 0)
@@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const cha
     if (strstart(filename, "tcp:", &p)) {
         return qemu_chr_open_tcp(p, 0);
     } else
-    if (strstart(filename, "tcpl:", &p)) {
+    if (strstart(filename, "telnet:", &p)) {
         return qemu_chr_open_tcp(p, 1);
     } else
     if (strstart(filename, "udp:", &p)) {
@@ -2618,6 +2671,45 @@ static int get_str_sep(char *buf, int bu
     return 0;
 }
 
+int parse_host_src_port(struct sockaddr_in *haddr,
+                        struct sockaddr_in *saddr,
+                        const char *input_str)
+{
+    char *str = strdup(input_str);
+    char *host_str = str;
+    char *src_str;
+    char *ptr;
+
+    /*
+     * Chop off any extra arguments at the end of the string which
+     * would start with a comma, then fill in the src port information
+     * if it was provided else use the "any address" and "any port".
+     */
+    if ((ptr = strchr(str,',')))
+        *ptr = '\0';
+
+    if ((src_str = strchr(input_str,'@'))) {
+        *src_str = '\0';
+        src_str++;
+    }
+
+    if (parse_host_port(haddr, host_str) < 0)
+        goto fail;
+
+    if (!src_str || *src_str == '\0')
+        src_str = ":0";
+
+    if (parse_host_port(saddr, src_str) < 0)
+        goto fail;
+
+    free(str);
+    return(0);
+
+fail:
+    free(str);
+    return -1;
+}
+
 int parse_host_port(struct sockaddr_in *saddr, const char *str)
 {
     char buf[512];
Index: qemu/qemu-doc.texi
===================================================================
--- qemu.orig/qemu-doc.texi
+++ qemu/qemu-doc.texi
@@ -525,43 +525,60 @@ Write output to filename. No character c
 name pipe @var{filename}
 @item COMn
 [Windows only] Use host serial port @var{n}
address@hidden udp:remote_port
-UDP Net Console sent to locahost at remote_port
address@hidden udp:remote_host:remote_port
-UDP Net Console sent to remote_host at remote_port
address@hidden udp:src_port:remote_host:remote_port
-UDP Net Console sent from src_port to remote_host at the remote_port.
-
-The udp:* sub options are primary intended for netconsole.  If you
-just want a simple readonly console you can use @code{netcat} or
address@hidden, by starting qemu with: @code{-serial udp:4555} and nc as:
address@hidden -u -l -p 4555}. Any time qemu writes something to that port
-it will appear in the netconsole session.
address@hidden udp:[remote_host]:remote_port[@@[src_ip]:src_port]
+This implements UDP Net Console.  When @var{remote_host} or @var{src_ip} are 
not specified they default to @code{0.0.0.0}.  When not using a specifed 
@var{src_port} a random port is automatically chosen.
+
+If you just want a simple readonly console you can use @code{netcat} or
address@hidden, by starting qemu with: @code{-serial udp::4555} and nc as:
address@hidden -u -l -p 4555}. Any time qemu writes something to that port it
+will appear in the netconsole session.
 
 If you plan to send characters back via netconsole or you want to stop
 and start qemu a lot of times, you should have qemu use the same
 source port each time by using something like @code{-serial
-udp:4556:localhost:4555} to qemu. Another approach is to use a patched
+udp::4555@@:4556} to qemu. Another approach is to use a patched
 version of netcat which can listen to a TCP port and send and receive
 characters via udp.  If you have a patched version of netcat which
 activates telnet remote echo and single char transfer, then you can
 use the following options to step up a netcat redirector to allow
 telnet on port 5555 to access the qemu port.
 @table @code
address@hidden Qemu Options
--serial udp:4556:localhost:4555
address@hidden netcat options
--u -P 4555 -L localhost:4556  -t -p 5555 -I -T
address@hidden Qemu Options:
+-serial udp::4555@@:4556
address@hidden netcat options:
+-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T
address@hidden telnet options:
+localhost 5555
address@hidden table
+
+
address@hidden tcp:[host]:port[,server][,nowait]
+The TCP Net Console has two modes of operation.  It can send the serial
+I/O to a location or wait for a connection from a location.  By default
+the TCP Net Console is sent to @var{host} at the @var{port}.  If you use
+the @var{,server} option QEMU will wait for a client socket application
+to connect to the port before continuing, unless the @code{,nowait}
+option was specified. If @var{host} is omitted, 0.0.0.0 is assumed. Only
+one TCP connection at a time is accepted. You can use @code{telnet} to
+connect to the corresponding character device.
address@hidden @code
address@hidden Example to send tcp console to 192.168.0.2 port 4444
+-serial tcp:192.168.0.2:4444
address@hidden Example to listen and wait on port 4444 for connection
+-serial tcp::4444,server
address@hidden Example to not wait and listen on ip 192.168.0.100 port 4444
+-serial tcp:192.168.0.100:4444,server,nowait
 @end table
 
address@hidden telnet:host:port[,server][,nowait]
+The telnet protocol is used instead of raw tcp sockets.  The options
+work the same as if you had specified @code{-serial tcp}.  The
+difference is that the port acts like a telnet server or client using
+telnet option negotiation.  This will also allow you to send the
+MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
+sequence.  Typically in unix telnet you do it with Control-] and then
+type "send break" followed by pressing the enter key.
 
address@hidden tcp:remote_host:remote_port
-TCP Net Console sent to remote_host at the remote_port
address@hidden tcpl:host:port
-TCP Net Console: wait for connection on @var{host} on the local port
address@hidden If host is omitted, 0.0.0.0 is assumed. Only one TCP
-connection at a time is accepted. You can use @code{telnet} to connect
-to the corresponding character device.
 @end table
 
 @item -parallel dev

reply via email to

[Prev in Thread] Current Thread [Next in Thread]