telnet protocol and more consistent syntax (Jason Wessel)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2030 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
		
							parent
							
								
									1f6e24e73c
								
							
						
					
					
						commit
						951f13516a
					
				| 
						 | 
				
			
			@ -525,43 +525,60 @@ Write output to filename. No character can be read.
 | 
			
		|||
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.
 | 
			
		||||
@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.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
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: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.
 | 
			
		||||
@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.
 | 
			
		||||
 | 
			
		||||
@end table
 | 
			
		||||
 | 
			
		||||
@item -parallel dev
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										202
									
								
								vl.c
								
								
								
								
							
							
						
						
									
										202
									
								
								vl.c
								
								
								
								
							| 
						 | 
				
			
			@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
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 char *def)
 | 
			
		|||
        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");
 | 
			
		||||
    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 *opaque)
 | 
			
		|||
    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,6 +2365,9 @@ static void tcp_chr_read(void *opaque)
 | 
			
		|||
        closesocket(s->fd);
 | 
			
		||||
        s->fd = -1;
 | 
			
		||||
    } else if (size > 0) {
 | 
			
		||||
        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(CharDriverState *chr)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
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,6 +2482,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
 | 
			
		|||
    fd = socket(PF_INET, SOCK_STREAM, 0);
 | 
			
		||||
    if (fd < 0) 
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    if (!is_waitconnect)
 | 
			
		||||
        socket_set_nonblock(fd);
 | 
			
		||||
 | 
			
		||||
    s->connected = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
 | 
			
		|||
            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_tcp(const char *host_str,
 | 
			
		|||
    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 char *filename)
 | 
			
		|||
    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 buf_size, const char **pp, int sep)
 | 
			
		|||
    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];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue