From: Jason Wessel <jason.wessel@windriver.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] telnet IAC options and simplified tcp & udp options
Date: Tue, 27 Jun 2006 09:53:16 -0500 [thread overview]
Message-ID: <44A1465C.9090703@windriver.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 152 bytes --]
This patch simplifies the syntax of the tcp and udp options, as well as
adding the telnet protocol.
signed-off-by: jason.wessel@windriver.com
Jason.
[-- Attachment #2: telnet_IAC_options.patch --]
[-- Type: text/plain, Size: 13968 bytes --]
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}
-@item udp:remote_port
-UDP Net Console sent to locahost at remote_port
-@item udp:remote_host:remote_port
-UDP Net Console sent to remote_host at remote_port
-@item 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
-@code{nc}, by starting qemu with: @code{-serial udp:4555} and nc as:
-@code{nc -u -l -p 4555}. Any time qemu writes something to that port
-it will appear in the netconsole session.
+@item 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
+@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as:
+@code{nc -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
-@item Qemu Options
--serial udp:4556:localhost:4555
-@item netcat options
--u -P 4555 -L localhost:4556 -t -p 5555 -I -T
+@item Qemu Options:
+-serial udp::4555@@:4556
+@item netcat options:
+-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T
+@item telnet options:
+localhost 5555
+@end table
+
+
+@item 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.
+@table @code
+@item Example to send tcp console to 192.168.0.2 port 4444
+-serial tcp:192.168.0.2:4444
+@item Example to listen and wait on port 4444 for connection
+-serial tcp::4444,server
+@item 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
+@item 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.
-@item tcp:remote_host:remote_port
-TCP Net Console sent to remote_host at the remote_port
-@item tcpl:host:port
-TCP Net Console: wait for connection on @var{host} on the local port
-@var{port}. 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 other threads:[~2006-06-27 14:53 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=44A1465C.9090703@windriver.com \
--to=jason.wessel@windriver.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.