char: don't assume telnet initialization will not block
The current code for doing telnet initialization is writing to a socket without checking the return status. While it is highly unlikely to be a problem when writing to a bare socket, as the buffers are large enough to prevent blocking, this cannot be assumed safe with TLS sockets. So write the telnet initialization code into a memory buffer and then use an I/O watch to fully send the data. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1453202071-10289-4-git-send-email-berrange@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
9894dc0cdc
commit
f2001a7e05
87
qemu-char.c
87
qemu-char.c
@ -2877,19 +2877,70 @@ static void tcp_chr_update_read_handler(CharDriverState *chr)
|
||||
}
|
||||
}
|
||||
|
||||
#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
|
||||
static void tcp_chr_telnet_init(QIOChannel *ioc)
|
||||
typedef struct {
|
||||
CharDriverState *chr;
|
||||
char buf[12];
|
||||
size_t buflen;
|
||||
} TCPCharDriverTelnetInit;
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
GIOCondition cond G_GNUC_UNUSED,
|
||||
gpointer user_data)
|
||||
{
|
||||
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 */
|
||||
qio_channel_write(ioc, buf, 3, NULL);
|
||||
IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
|
||||
qio_channel_write(ioc, buf, 3, NULL);
|
||||
IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
|
||||
qio_channel_write(ioc, buf, 3, NULL);
|
||||
IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
|
||||
qio_channel_write(ioc, buf, 3, NULL);
|
||||
TCPCharDriverTelnetInit *init = user_data;
|
||||
ssize_t ret;
|
||||
|
||||
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
|
||||
if (ret < 0) {
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
tcp_chr_disconnect(init->chr);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
init->buflen -= ret;
|
||||
|
||||
if (init->buflen == 0) {
|
||||
tcp_chr_connect(init->chr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memmove(init->buf, init->buf + ret, init->buflen);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void tcp_chr_telnet_init(CharDriverState *chr)
|
||||
{
|
||||
TCPCharDriver *s = chr->opaque;
|
||||
TCPCharDriverTelnetInit *init =
|
||||
g_new0(TCPCharDriverTelnetInit, 1);
|
||||
size_t n = 0;
|
||||
|
||||
init->chr = chr;
|
||||
init->buflen = 12;
|
||||
|
||||
#define IACSET(x, a, b, c) \
|
||||
do { \
|
||||
x[n++] = a; \
|
||||
x[n++] = b; \
|
||||
x[n++] = c; \
|
||||
} while (0)
|
||||
|
||||
/* Prep the telnet negotion to put telnet in binary,
|
||||
* no echo, single char mode */
|
||||
IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
|
||||
IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
|
||||
IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
|
||||
IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
|
||||
|
||||
#undef IACSET
|
||||
|
||||
qio_channel_add_watch(
|
||||
s->ioc, G_IO_OUT,
|
||||
tcp_chr_telnet_init_io,
|
||||
init, NULL);
|
||||
}
|
||||
|
||||
static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
|
||||
@ -2909,7 +2960,12 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
|
||||
g_source_remove(s->listen_tag);
|
||||
s->listen_tag = 0;
|
||||
}
|
||||
tcp_chr_connect(chr);
|
||||
|
||||
if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2935,7 +2991,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
|
||||
void *opaque)
|
||||
{
|
||||
CharDriverState *chr = opaque;
|
||||
TCPCharDriver *s = chr->opaque;
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
|
||||
@ -2944,10 +2999,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(QIO_CHANNEL(sioc));
|
||||
}
|
||||
|
||||
tcp_chr_new_client(chr, sioc);
|
||||
|
||||
object_unref(OBJECT(sioc));
|
||||
|
Loading…
Reference in New Issue
Block a user