mirror of https://github.com/xemu-project/xemu.git
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;
|
typedef struct {
|
||||||
static void tcp_chr_telnet_init(QIOChannel *ioc)
|
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];
|
TCPCharDriverTelnetInit *init = user_data;
|
||||||
/* Send the telnet negotion to put telnet in binary, no echo, single char mode */
|
ssize_t ret;
|
||||||
IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
|
|
||||||
qio_channel_write(ioc, buf, 3, NULL);
|
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
|
||||||
IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
|
if (ret < 0) {
|
||||||
qio_channel_write(ioc, buf, 3, NULL);
|
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
|
ret = 0;
|
||||||
qio_channel_write(ioc, buf, 3, NULL);
|
} else {
|
||||||
IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
|
tcp_chr_disconnect(init->chr);
|
||||||
qio_channel_write(ioc, buf, 3, NULL);
|
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)
|
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);
|
g_source_remove(s->listen_tag);
|
||||||
s->listen_tag = 0;
|
s->listen_tag = 0;
|
||||||
}
|
}
|
||||||
tcp_chr_connect(chr);
|
|
||||||
|
if (s->do_telnetopt) {
|
||||||
|
tcp_chr_telnet_init(chr);
|
||||||
|
} else {
|
||||||
|
tcp_chr_connect(chr);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2935,7 +2991,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
CharDriverState *chr = opaque;
|
CharDriverState *chr = opaque;
|
||||||
TCPCharDriver *s = chr->opaque;
|
|
||||||
QIOChannelSocket *sioc;
|
QIOChannelSocket *sioc;
|
||||||
|
|
||||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
|
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
|
||||||
|
@ -2944,10 +2999,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->do_telnetopt) {
|
|
||||||
tcp_chr_telnet_init(QIO_CHANNEL(sioc));
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp_chr_new_client(chr, sioc);
|
tcp_chr_new_client(chr, sioc);
|
||||||
|
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
|
|
Loading…
Reference in New Issue