All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver
@ 2015-04-01 12:09 Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 1/5] " Max Reitz
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

This series adds a long-missing IRC character device driver to qemu. See
patch 1 for an explanation why you should have been missing it.

To make it short: IRC is a very well tested and more reliable, social,
human-friendly, and ubiquitous[1] communication interface than any of
the other character devices qemu has to offer.

"Social?", you ask? "Exactly!", I say. Again, see patch 1.

"But why do we need 'social'?" Because what is it that separates qemu
from the other well-known type-2 hypervisors? Besides simply being
better (of course!), it is free software which is written by and
maintained by a community. It is social.

Now we need to capitalize on this very fact even further, not only
embracing it in qemu development, but also in qemu usage.

Already, everyone can work on the qemu project.

In the future, everyone will be able to work with a qemu instance.

This is what this patch series embodies. The future is now.

What a time to be alive.


Oh, and don't forget the reliability and ubiquity.


Finally, there has been no encryption whatsoever in qemu's character
devices so far. For IRC, SSL is a well-known and commonly implemented
client-to-server encryption method (patch 3), and this series will
furthermore introduce a widely used and easy to implement, yet secure
enough even for military standards, end-to-end encryption (patch 4).


[1] Google search for:
     - irc: 1.27e+8 results
     - tcp: 1.08e+8 results
     - udp: 6.28e+7 results
     - telnet: 2.29e+7 results
     - stdio: 9.04e+6 results
     - "unix sockets": 1.37e+5 results


Max Reitz (5):
  chardev: Add IRC char driver
  chardev/irc: Add sockfd option
  chardev/irc: Add SSL support
  chardev/irc: Add end-to-end encryption
  Documentation: Document IRC char driver

 configure        |  43 +++-
 qapi-schema.json |  32 +++
 qemu-char.c      | 714 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx  |  28 +++
 4 files changed, 806 insertions(+), 11 deletions(-)

-- 
2.3.4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH for-2.4 1/5] chardev: Add IRC char driver
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
@ 2015-04-01 12:09 ` Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 2/5] chardev/irc: Add sockfd option Max Reitz
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

In order for qemu to become more human-friendly, we require an interface
which is actually used by humans in their normal day-to-day
communication. Many people in the qemu community will agree that IRC is
indeed such a communication platform. By adding that support to qemu,
users no longer have to switch from IRC to command line and back to
debug QMP issues and the like while discussing it in the #qemu channel.

Furthermore, debugging has never been this social: In the past, people
needed to ask the debugging person to try specific commands and that
person had to paste the reply back to IRC. Now, you can simply let qemu
connect to a discussion channel and everyone can participate in
debugging.

And finally, there are technical merits as well: IRC is a well-tested,
fault-tolerant network with redundant nodes. This gives us much greater
reliability than any of the existing character devices, which is
especially important for remote debugging.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qapi-schema.json |  16 ++
 qemu-char.c      | 492 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 508 insertions(+)

diff --git a/qapi-schema.json b/qapi-schema.json
index ac9594d..f427e04 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2876,6 +2876,21 @@
 { 'type': 'ChardevRingbuf', 'data': { '*size'  : 'int' } }
 
 ##
+# @ChardevIrc
+#
+# Configuration info for IRC chardevs.
+#
+# @addr:    IRC server
+# @nick:    Nick for qemu to use
+# @channel: Channel to join, or nick to query
+#
+# Since: 2.4
+##
+{ 'type': 'ChardevIrc', 'data': { 'addr'        : 'SocketAddress',
+                                  'nick'        : 'str',
+                                  'channel'     : 'str' } }
+
+##
 # @ChardevBackend:
 #
 # Configuration info for the new chardev backend.
@@ -2902,6 +2917,7 @@
                                        'spiceport' : 'ChardevSpicePort',
                                        'vc'     : 'ChardevVC',
                                        'ringbuf': 'ChardevRingbuf',
+                                       'irc'    : 'ChardevIrc',
                                        # next one is just for compatibility
                                        'memory' : 'ChardevRingbuf' } }
 
diff --git a/qemu-char.c b/qemu-char.c
index a405d76..a68fef3 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3296,6 +3296,296 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
     return data;
 }
 
+/*********************************************************/
+/* IRC chardev */
+
+typedef struct {
+    int fd;
+    bool query;
+    char *nick, *channel;
+
+    GIOChannel *chan;
+
+    uint8_t *sendbuf;
+    int sendbuf_idx, sendbuf_prefixlen;
+    bool send_line_skip;
+
+    uint8_t *recvbuf;
+    int recvbuf_idx;
+    bool recv_line_skip;
+} IrcCharDriverState;
+
+static ssize_t irc_send(IrcCharDriverState *irc, const void *buf, size_t len)
+{
+    return send_all(irc->fd, buf, len);
+}
+
+static ssize_t irc_recv(IrcCharDriverState *irc, void *buf, size_t len)
+{
+    return recv_all(irc->fd, buf, len, true);
+}
+
+static int irc_read_line(IrcCharDriverState *irc, char **buf)
+{
+    ssize_t ret;
+    int eol_idx = -1, i;
+
+    for (i = 0; i < irc->recvbuf_idx; i++) {
+        if (irc->recvbuf[i] == '\n' || irc->recvbuf[i] == '\r') {
+            break;
+        }
+    }
+
+    if (i < irc->recvbuf_idx) {
+        eol_idx = i;
+    }
+
+    while (eol_idx < 0) {
+        if (irc->recvbuf_idx >= 512) {
+            irc->recvbuf_idx = 0;
+            irc->recv_line_skip = true;
+        }
+
+        ret = irc_recv(irc, irc->recvbuf + irc->recvbuf_idx,
+                       512 - irc->recvbuf_idx);
+        if (ret < 0) {
+            irc->recvbuf_idx = 0;
+            return ret;
+        }
+
+        for (i = 0; i < ret; i++) {
+            if (irc->recvbuf[irc->recvbuf_idx + i] == '\n' ||
+                irc->recvbuf[irc->recvbuf_idx + i] == '\r')
+            {
+                break;
+            }
+        }
+        irc->recvbuf_idx += ret;
+
+        if (i < ret) {
+            eol_idx = irc->recvbuf_idx - ret + i;
+            break;
+        }
+    }
+
+    if (!irc->recv_line_skip) {
+        *buf = g_new(char, 512);
+        memcpy(*buf, irc->recvbuf, eol_idx);
+        (*buf)[eol_idx] = 0;
+    }
+
+    /* CRLF is the standard, but supporting LF only will not hurt */
+    if (irc->recvbuf[++eol_idx] == '\n') {
+        ++eol_idx;
+    }
+
+    memmove(irc->recvbuf, irc->recvbuf + eol_idx, 512 - eol_idx);
+    irc->recvbuf_idx -= eol_idx;
+    irc->recvbuf[irc->recvbuf_idx] = 0;
+
+    if (irc->recv_line_skip) {
+        irc->recv_line_skip = false;
+        return -EPROTO;
+    }
+    return 0;
+}
+
+static int irc_chr_write(CharDriverState *s, const uint8_t *buf, int len)
+{
+    IrcCharDriverState *irc = s->opaque;
+    static const int max_line_length = 384; /* 512 minus the prefix */
+    int i;
+
+    if (!len) {
+        return 0;
+    }
+
+    for (i = 0; i < len; i++) {
+        if (buf[i] == '\r') {
+            continue;
+        } else if (buf[i] == '\n') {
+            if (irc->sendbuf) {
+                if (!irc->send_line_skip) {
+                    irc->sendbuf[irc->sendbuf_idx++] = '\r';
+                    irc->sendbuf[irc->sendbuf_idx++] = '\n';
+
+                    /* Ignore errors */
+                    irc_send(irc, irc->sendbuf, irc->sendbuf_idx);
+                }
+
+                irc->sendbuf_idx = irc->sendbuf_prefixlen;
+                irc->send_line_skip = false;
+            }
+        } else if (iscntrl(buf[i])) {
+            /* IRC is no terminal, control characters do not make a whole lot
+             * of sense; just skip those lines */
+            irc->send_line_skip = true;
+        } else {
+            assert(irc->sendbuf_idx <= max_line_length);
+
+            if (irc->sendbuf_idx == max_line_length) {
+                int break_index;
+
+                for (break_index = max_line_length - 1; break_index > 0;
+                     break_index--)
+                {
+                    if (isspace(irc->sendbuf[break_index])) {
+                        break;
+                    }
+                }
+                if (break_index > 0) {
+                    irc->sendbuf[break_index] = 0;
+                } else {
+                    break_index = max_line_length;
+                }
+
+                if (!irc->send_line_skip) {
+                    char replaced = irc->sendbuf[break_index + 1];
+
+                    irc->sendbuf[break_index] = '\r';
+                    irc->sendbuf[break_index + 1] = '\n';
+
+                    irc_send(irc, irc->sendbuf, break_index + 2);
+
+                    irc->sendbuf[break_index + 1] = replaced;
+                }
+
+                memmove(&irc->sendbuf[irc->sendbuf_prefixlen],
+                        &irc->sendbuf[break_index + 1],
+                        max_line_length - break_index - 1);
+
+                irc->sendbuf_idx = irc->sendbuf_prefixlen +
+                                   max_line_length - break_index - 1;
+            }
+
+            irc->sendbuf[irc->sendbuf_idx++] = buf[i];
+        }
+    }
+
+    return len;
+}
+
+static int irc_chr_read_poll(void *opaque)
+{
+    CharDriverState *s = opaque;
+    return qemu_chr_be_can_write(s);
+}
+
+static gboolean irc_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    CharDriverState *s = opaque;
+    IrcCharDriverState *irc = s->opaque;
+    char *cmd, *src = NULL, *dest = NULL, *msg = NULL;
+    char *cmd_save = NULL, *line, real_src[128], real_dest[128], *buffer;
+    bool in_query;
+    int i, ret;
+
+    ret = irc_read_line(irc, &buffer);
+    if (ret < 0) {
+        return FALSE;
+    }
+    line = buffer;
+
+    cmd = strtok_r(line, " ", &cmd_save);
+    if (cmd && cmd[0] == ':') {
+        src = cmd + 1;
+        cmd = strtok_r(NULL, " ", &cmd_save);
+    }
+
+    if (!cmd) {
+        g_free(buffer);
+        return FALSE;
+    }
+
+    if (!strcmp(cmd, "PING")) {
+        char *pong = g_strdup_printf("PONG %s\r\n", cmd + strlen(cmd) + 1);
+        /* Ignore errors */
+        irc_send(irc, pong, strlen(pong));
+        g_free(pong);
+        return TRUE;
+    }
+
+    dest = strtok_r(NULL, " ", &cmd_save);
+    if (!dest || dest[0] == ':') {
+        g_free(buffer);
+        return FALSE;
+    }
+
+    msg = dest + strlen(dest) + 1;
+    if (msg[0] == ':') {
+        msg++;
+    } else {
+        msg = strtok_r(NULL, " ", &cmd_save);
+    }
+
+    for (i = 0; src[i] && src[i] != '!' && i < 127; i++) {
+        real_src[i] = src[i];
+    }
+    real_src[i] = 0;
+
+    for (i = 0; dest[i] && dest[i] != '!' && i < 127; i++) {
+        real_dest[i] = dest[i];
+    }
+    real_dest[i] = 0;
+
+    (void)real_src;
+
+    in_query = !strcmp(real_dest, irc->nick);
+
+    /* ignore CTCP */
+    if (!strcmp(cmd, "PRIVMSG") && msg[0] != 1 &&
+        (in_query || (!strncmp(msg, irc->nick, strlen(irc->nick)) &&
+                      !isalnum(msg[strlen(irc->nick)]))))
+    {
+        const char *start = msg;
+
+        if (!in_query) {
+            start = msg + strlen(irc->nick) + 1;
+            while (*start && isspace(*start)) {
+                start++;
+            }
+        }
+
+        qemu_chr_be_write(s, (uint8_t *)start, strlen(start));
+        qemu_chr_be_write(s, (uint8_t *)"\n", 1);
+    }
+
+    g_free(buffer);
+    return TRUE;
+}
+
+static GSource *irc_chr_add_watch(CharDriverState *s, GIOCondition cond)
+{
+    IrcCharDriverState *irc = s->opaque;
+    return g_io_create_watch(irc->chan, cond);
+}
+
+static void irc_chr_update_read_handler(CharDriverState *s)
+{
+    IrcCharDriverState *irc = s->opaque;
+    io_add_watch_poll(irc->chan, irc_chr_read_poll, irc_chr_read, s);
+}
+
+static void irc_destroy(IrcCharDriverState *irc)
+{
+    irc_send(irc, "QUIT\r\n", strlen("QUIT\r\n"));
+    g_io_channel_unref(irc->chan);
+    close(irc->fd);
+
+    g_free(irc->nick);
+    g_free(irc->channel);
+    g_free(irc->sendbuf);
+    g_free(irc->recvbuf);
+
+    g_free(irc);
+}
+
+static void irc_chr_close(CharDriverState *s)
+{
+    irc_destroy(s->opaque);
+    s->opaque = NULL;
+}
+
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
 {
     char host[65], port[33], width[8], height[8];
@@ -3433,6 +3723,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
         return opts;
     }
 
+    if (strstart(filename, "irc,", &p)) {
+        qemu_opts_do_parse(opts, p, NULL, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            goto fail;
+        }
+        qemu_opt_set(opts, "backend", "irc", &error_abort);
+        return opts;
+    }
+
 fail:
     qemu_opts_del(opts);
     return NULL;
@@ -3634,6 +3934,40 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
+static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
+                               Error **errp)
+{
+    const char *host, *port, *nick, *channel;
+    SocketAddress *addr;
+
+    host    = qemu_opt_get(opts, "host");
+    port    = qemu_opt_get(opts, "port") ?: "6667";
+    nick    = qemu_opt_get(opts, "nick");
+    channel = qemu_opt_get(opts, "channel");
+
+    if (!host || !nick || !channel) {
+        error_setg(errp, "chardev: irc: Missing options");
+        return;
+    }
+
+    if (strlen(nick) > 64 || strlen(channel) > 64) {
+        error_setg(errp, "chardev: irc: Nick or channel too long");
+        return;
+    }
+
+    backend->irc = g_new0(ChardevIrc, 1);
+
+    addr = g_new0(SocketAddress, 1);
+    addr->kind = SOCKET_ADDRESS_KIND_INET;
+    addr->inet = g_new0(InetSocketAddress, 1);
+    addr->inet->host = g_strdup(host);
+    addr->inet->port = g_strdup(port);
+    backend->irc->addr = addr;
+
+    backend->irc->nick = g_strdup(nick);
+    backend->irc->channel = g_strdup(channel);
+}
+
 typedef struct CharDriver {
     const char *name;
     ChardevBackendKind kind;
@@ -3992,6 +4326,12 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "chardev",
             .type = QEMU_OPT_STRING,
+        },{
+            .name = "nick",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "channel",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -4206,6 +4546,154 @@ static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
     return qemu_chr_open_udp_fd(fd);
 }
 
+#ifdef CONFIG_GNUTLS
+static ssize_t push_all(gnutls_transport_ptr_t tptr,
+                        const void *buf, size_t len)
+{
+    return send_all((uintptr_t)tptr, buf, len);
+}
+
+static ssize_t pull_all(gnutls_transport_ptr_t tptr, void *buf, size_t len)
+{
+    return recv_all((uintptr_t)tptr, buf, len, true);
+}
+
+static int gnutls_cert_allow_all(gnutls_session_t session)
+{
+    return 0;
+}
+#endif
+
+static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
+{
+    IrcCharDriverState *irc_cds;
+    CharDriverState *s;
+    Error *local_err = NULL;
+    GIOChannel *chan;
+    char *sendstr, *buffer = NULL;
+    bool joined = false, query;
+    int fd, ret;
+
+    fd = socket_connect(irc->addr, &local_err, NULL, NULL);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return NULL;
+    }
+
+#ifdef _WIN32
+    chan = g_io_channel_win32_new_socket(fd);
+#else
+    chan = g_io_channel_unix_new(fd);
+#endif
+
+    irc_cds = g_new0(IrcCharDriverState, 1);
+    irc_cds->fd      = fd;
+    irc_cds->nick    = g_strdup(irc->nick);
+    irc_cds->channel = g_strdup(irc->channel);
+
+    irc_cds->chan = chan;
+
+    irc_cds->sendbuf = g_malloc(512);
+    irc_cds->sendbuf_prefixlen = sprintf((char *)irc_cds->sendbuf,
+                                         "PRIVMSG %s :", irc_cds->channel);
+    irc_cds->sendbuf_idx = irc_cds->sendbuf_prefixlen;
+
+    irc_cds->recvbuf = g_malloc(1024);
+    irc_cds->recvbuf_idx = 0;
+
+    sendstr = g_strdup_printf("USER qemu qemu qemu qemu\r\nNICK %s\r\n",
+                              irc->nick);
+    ret = irc_send(irc_cds, sendstr, strlen(sendstr));
+    g_free(sendstr);
+    if (ret < 0) {
+        error_setg(errp, "Failed to send IRC USER+NICK");
+        goto fail;
+    }
+
+    query = irc->channel[0] != '#' && irc->channel[0] != '&' &&
+            irc->channel[0] != '+' && irc->channel[0] != '!';
+
+    while (!joined) {
+        const char *pars, *cmd, *src = NULL;
+        char *cmd_save = NULL;
+
+        ret = irc_read_line(irc_cds, &buffer);
+        if (ret < 0) {
+            continue;
+        }
+
+        cmd = strtok_r(buffer, " ", &cmd_save);
+        if (cmd && cmd[0] == ':') {
+            src = cmd + 1;
+            cmd = strtok_r(NULL, " ", &cmd_save);
+        }
+
+        if (!cmd) {
+            continue;
+        }
+
+        pars = cmd + strlen(cmd) + 1;
+
+        if (!strcmp(cmd, "PING")) {
+            sendstr = g_strdup_printf("PONG %s\r\n", pars);
+            ret = irc_send(irc_cds, sendstr, strlen(sendstr));
+            g_free(sendstr);
+            if (ret < 0) {
+                error_setg_errno(errp, errno, "Failed to send IRC PONG");
+                goto fail;
+            }
+        } else if (!strcmp(cmd, "376")) {
+            /* Ignore erros */
+            irc_send(irc_cds, "MODE qemu42 +B qemu42\r\n",
+                     strlen("MODE qemu42 +B qemu42\r\n"));
+
+            if (query) {
+                joined = true;
+            } else {
+                sendstr = g_strdup_printf("JOIN %s\r\n", irc->channel);
+                ret = irc_send(irc_cds, sendstr, strlen(sendstr));
+                g_free(sendstr);
+                if (ret < 0) {
+                    error_setg_errno(errp, errno, "Failed to send IRC JOIN");
+                    goto fail;
+                }
+            }
+        } else if (!strcmp(cmd, "JOIN")) {
+            char real_src[128];
+            int i;
+
+            for (i = 0; src[i] && src[i] != '!' && i < 127; i++) {
+                real_src[i] = src[i];
+            }
+            real_src[i] = 0;
+
+            if (!strcmp(real_src, irc->nick)) {
+                joined = true;
+            }
+        }
+
+        g_free(buffer);
+        buffer = NULL;
+    }
+
+    irc_cds->query = query;
+
+    s = qemu_chr_alloc();
+    s->chr_write = irc_chr_write;
+    s->chr_close = irc_chr_close;
+    s->chr_add_watch = irc_chr_add_watch;
+    s->chr_update_read_handler = irc_chr_update_read_handler;
+
+    s->opaque = irc_cds;
+
+    return s;
+
+
+fail:
+    irc_destroy(irc_cds);
+    return NULL;
+}
+
 ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
                                Error **errp)
 {
@@ -4289,6 +4777,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
     case CHARDEV_BACKEND_KIND_MEMORY:
         chr = qemu_chr_open_ringbuf(backend->ringbuf, errp);
         break;
+    case CHARDEV_BACKEND_KIND_IRC:
+        chr = qemu_chr_open_irc(backend->irc, errp);
+        break;
     default:
         error_setg(errp, "unknown chardev backend (%d)", backend->kind);
         break;
@@ -4366,6 +4857,7 @@ static void register_types(void)
     /* Bug-compatibility: */
     register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
                          qemu_chr_parse_ringbuf);
+    register_char_driver("irc", CHARDEV_BACKEND_KIND_IRC, qemu_chr_parse_irc);
     /* this must be done after machine init, since we register FEs with muxes
      * as part of realize functions like serial_isa_realizefn when -nographic
      * is specified
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH for-2.4 2/5] chardev/irc: Add sockfd option
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 1/5] " Max Reitz
@ 2015-04-01 12:09 ` Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 3/5] chardev/irc: Add SSL support Max Reitz
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

IRC being well on the way to deprecate other character devices, it must
be exposed to management tools as well. These often prefer to pass a
socket FD instead of a host and port to connect to, so this patch adds
an appropriate option.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qemu-char.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index a68fef3..4507e5f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3937,19 +3937,26 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
 static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
-    const char *host, *port, *nick, *channel;
+    const char *host, *port, *nick, *channel, *sockfd;
     SocketAddress *addr;
 
     host    = qemu_opt_get(opts, "host");
     port    = qemu_opt_get(opts, "port") ?: "6667";
+    sockfd  = qemu_opt_get(opts, "sockfd");
     nick    = qemu_opt_get(opts, "nick");
     channel = qemu_opt_get(opts, "channel");
 
-    if (!host || !nick || !channel) {
+    if ((!host && !sockfd) || !nick || !channel) {
         error_setg(errp, "chardev: irc: Missing options");
         return;
     }
 
+    if ((host || port) && sockfd) {
+        error_setg(errp,
+                   "chardev: irc: Both sockfd and host and/or port specified");
+        return;
+    }
+
     if (strlen(nick) > 64 || strlen(channel) > 64) {
         error_setg(errp, "chardev: irc: Nick or channel too long");
         return;
@@ -3958,10 +3965,17 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     backend->irc = g_new0(ChardevIrc, 1);
 
     addr = g_new0(SocketAddress, 1);
-    addr->kind = SOCKET_ADDRESS_KIND_INET;
-    addr->inet = g_new0(InetSocketAddress, 1);
-    addr->inet->host = g_strdup(host);
-    addr->inet->port = g_strdup(port);
+    if (host) {
+        addr->kind = SOCKET_ADDRESS_KIND_INET;
+        addr->inet = g_new0(InetSocketAddress, 1);
+        addr->inet->host = g_strdup(host);
+        addr->inet->port = g_strdup(port);
+    } else {
+        addr->kind = SOCKET_ADDRESS_KIND_FD;
+        addr->fd = g_new(String, 1);
+        addr->fd->str = g_strdup(sockfd);
+    }
+
     backend->irc->addr = addr;
 
     backend->irc->nick = g_strdup(nick);
@@ -4332,6 +4346,9 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "channel",
             .type = QEMU_OPT_STRING,
+        },{
+            .name = "sockfd",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH for-2.4 3/5] chardev/irc: Add SSL support
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 1/5] " Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 2/5] chardev/irc: Add sockfd option Max Reitz
@ 2015-04-01 12:09 ` Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 4/5] chardev/irc: Add end-to-end encryption Max Reitz
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

qemu already makes use of gnutls, so it is only natural to extend its
use to the IRC char driver. Furthermore, though IRC is ubiquitous, there
may be servers which allow only SSL connections for some sense of
additional security, be it real or not. Adding that support to qemu
therefore means greater compatibility which should be the goal of any
software implementing an open standard such as IRC.

The Heartbleed crisis influences this patch in two ways: First, it does
not use OpenSSL. As GnuTLS is not nearly used as commonly, this will
give us an advantage regarding attackers which prefer to attack the most
widespread platform. Second, certificates can be stolen any time and
revoking them does not work. Therefore, authentication in SSL is broken
by design and we do not need to care for it. This guideline is generally
followed by IRC servers as well, for instance OFTC, which hosts the
#qemu IRC channel, does offer a self-signed certificate. Thus, this
policy seems widely accepted and to be current industry practice.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 configure        |  43 ++++++++++++++++------
 qapi-schema.json |   3 +-
 qemu-char.c      | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+), 12 deletions(-)

diff --git a/configure b/configure
index 09c9225..63ea221 100755
--- a/configure
+++ b/configure
@@ -246,6 +246,7 @@ vnc_sasl=""
 vnc_jpeg=""
 vnc_png=""
 vnc_ws=""
+gnutls=""
 xen=""
 xen_ctrl_version=""
 xen_pci_passthrough=""
@@ -889,6 +890,10 @@ for opt do
   ;;
   --enable-vnc-ws) vnc_ws="yes"
   ;;
+  --disable-gnutls) gnutls="no"
+  ;;
+  --enable-gnutls) gnutls="yes"
+  ;;
   --disable-slirp) slirp="no"
   ;;
   --disable-uuid) uuid="no"
@@ -2283,32 +2288,43 @@ EOF
 fi
 
 ##########################################
-# VNC TLS/WS detection
-if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then
+# GnuTLS detection
+if test "$gnutls" != "no" ; then
   cat > $TMPC <<EOF
 #include <gnutls/gnutls.h>
 int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
 EOF
-  vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
-  vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
-  if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then
+  gnutls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
+  gnutls_libs=`$pkg_config --libs gnutls 2> /dev/null`
+  if compile_prog "$gnutls_cflags" "$gnutls_libs" ; then
+    gnutls=yes
+    libs_softmmu="$gnutls_libs $libs_softmmu"
+    QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags"
+  else
+    if test "$gnutls" = "yes" ; then
+      feature_not_found "gnutls" "Install gnutls devel"
+    fi
+    gnutls=no
+  fi
+fi
+
+##########################################
+# VNC TLS/WS detection
+if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then
+  if test "$gnutls" = "yes" ; then
     if test "$vnc_tls" != "no" ; then
       vnc_tls=yes
     fi
     if test "$vnc_ws" != "no" ; then
       vnc_ws=yes
     fi
-    libs_softmmu="$vnc_tls_libs $libs_softmmu"
-    QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
   else
     if test "$vnc_tls" = "yes" ; then
-      feature_not_found "vnc-tls" "Install gnutls devel"
+      feature_not_found "vnc-tls" "Enable gnutls"
     fi
     if test "$vnc_ws" = "yes" ; then
-      feature_not_found "vnc-ws" "Install gnutls devel"
+      feature_not_found "vnc-ws" "Enable gnutls"
     fi
-    vnc_tls=no
-    vnc_ws=no
   fi
 fi
 
@@ -4440,6 +4456,7 @@ echo "lzo support       $lzo"
 echo "snappy support    $snappy"
 echo "bzip2 support     $bzip2"
 echo "NUMA host support $numa"
+echo "GnuTLS support    $gnutls"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -4921,6 +4938,10 @@ if test "$tpm" = "yes"; then
   fi
 fi
 
+if test "$gnutls" = "yes" ; then
+  echo "CONFIG_GNUTLS=y" >> $config_host_mak
+fi
+
 echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak
 if have_backend "nop"; then
   echo "CONFIG_TRACE_NOP=y" >> $config_host_mak
diff --git a/qapi-schema.json b/qapi-schema.json
index f427e04..e4f93fd 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2888,7 +2888,8 @@
 ##
 { 'type': 'ChardevIrc', 'data': { 'addr'        : 'SocketAddress',
                                   'nick'        : 'str',
-                                  'channel'     : 'str' } }
+                                  'channel'     : 'str',
+                                  'ssl'         : 'bool' } }
 
 ##
 # @ChardevBackend:
diff --git a/qemu-char.c b/qemu-char.c
index 4507e5f..1030d5e 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -81,6 +81,9 @@
 #endif
 #endif
 #endif
+#ifdef CONFIG_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
 
 #include "qemu/sockets.h"
 #include "ui/qemu-spice.h"
@@ -3306,6 +3309,11 @@ typedef struct {
 
     GIOChannel *chan;
 
+#ifdef CONFIG_GNUTLS
+    gnutls_session_t tls_session;
+    gnutls_certificate_credentials_t tls_cred;
+#endif
+
     uint8_t *sendbuf;
     int sendbuf_idx, sendbuf_prefixlen;
     bool send_line_skip;
@@ -3315,6 +3323,38 @@ typedef struct {
     bool recv_line_skip;
 } IrcCharDriverState;
 
+#ifdef CONFIG_GNUTLS
+static ssize_t irc_send(IrcCharDriverState *irc, const void *buf, size_t len)
+{
+    if (irc->tls_session) {
+        return gnutls_record_send(irc->tls_session, buf, len);
+    } else {
+        return send_all(irc->fd, buf, len);
+    }
+}
+
+static ssize_t irc_recv(IrcCharDriverState *irc, void *buf, size_t len)
+{
+    if (irc->tls_session) {
+        ssize_t ret = gnutls_record_recv(irc->tls_session, buf, len);
+        if (ret >= 0) {
+            return ret;
+        } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+            return irc_recv(irc, buf, len);
+        } else if (ret == GNUTLS_E_REHANDSHAKE) {
+            ret = gnutls_handshake(irc->tls_session);
+            if (ret != GNUTLS_E_SUCCESS) {
+                return -1;
+            }
+            return irc_recv(irc, buf, len);
+        } else {
+            return -1;
+        }
+    } else {
+        return recv_all(irc->fd, buf, len, true);
+    }
+}
+#else
 static ssize_t irc_send(IrcCharDriverState *irc, const void *buf, size_t len)
 {
     return send_all(irc->fd, buf, len);
@@ -3324,6 +3364,7 @@ static ssize_t irc_recv(IrcCharDriverState *irc, void *buf, size_t len)
 {
     return recv_all(irc->fd, buf, len, true);
 }
+#endif
 
 static int irc_read_line(IrcCharDriverState *irc, char **buf)
 {
@@ -3569,6 +3610,13 @@ static void irc_chr_update_read_handler(CharDriverState *s)
 static void irc_destroy(IrcCharDriverState *irc)
 {
     irc_send(irc, "QUIT\r\n", strlen("QUIT\r\n"));
+#ifdef CONFIG_GNUTLS
+    if (irc->tls_session) {
+        gnutls_bye(irc->tls_session, GNUTLS_SHUT_RDWR);
+        gnutls_certificate_free_credentials(irc->tls_cred);
+        gnutls_deinit(irc->tls_session);
+    }
+#endif
     g_io_channel_unref(irc->chan);
     close(irc->fd);
 
@@ -3938,6 +3986,7 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
     const char *host, *port, *nick, *channel, *sockfd;
+    bool ssl;
     SocketAddress *addr;
 
     host    = qemu_opt_get(opts, "host");
@@ -3945,6 +3994,11 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     sockfd  = qemu_opt_get(opts, "sockfd");
     nick    = qemu_opt_get(opts, "nick");
     channel = qemu_opt_get(opts, "channel");
+#ifdef CONFIG_GNUTLS
+    ssl     = qemu_opt_get_bool(opts, "ssl", false);
+#else
+    ssl     = false;
+#endif
 
     if ((!host && !sockfd) || !nick || !channel) {
         error_setg(errp, "chardev: irc: Missing options");
@@ -3980,6 +4034,7 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
 
     backend->irc->nick = g_strdup(nick);
     backend->irc->channel = g_strdup(channel);
+    backend->irc->ssl = ssl;
 }
 
 typedef struct CharDriver {
@@ -4349,6 +4404,11 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "sockfd",
             .type = QEMU_OPT_STRING,
+#ifdef CONFIG_GNUTLS
+        },{
+            .name = "ssl",
+            .type = QEMU_OPT_BOOL,
+#endif
         },
         { /* end of list */ }
     },
@@ -4618,6 +4678,52 @@ static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
     irc_cds->recvbuf = g_malloc(1024);
     irc_cds->recvbuf_idx = 0;
 
+#ifdef CONFIG_GNUTLS
+    if (irc->ssl) {
+        ret = gnutls_init(&irc_cds->tls_session, GNUTLS_CLIENT);
+        if (ret != GNUTLS_E_SUCCESS) {
+            error_setg(errp, "Failed to initialize GnuTLS session: %s",
+                       gnutls_strerror(ret));
+            goto fail;
+        }
+
+        gnutls_transport_set_ptr(irc_cds->tls_session,
+                                 (gnutls_transport_ptr_t)(uintptr_t)fd);
+        gnutls_transport_set_push_function(irc_cds->tls_session, &push_all);
+        gnutls_transport_set_pull_function(irc_cds->tls_session, &pull_all);
+
+        ret = gnutls_priority_set_direct(irc_cds->tls_session, "NORMAL", NULL);
+        if (ret != GNUTLS_E_SUCCESS) {
+            error_setg(errp, "Setting TLS priorities failed: %s",
+                       gnutls_strerror(ret));
+            goto fail;
+        }
+
+        ret = gnutls_certificate_allocate_credentials(&irc_cds->tls_cred);
+        if (ret != GNUTLS_E_SUCCESS) {
+            error_setg(errp, "TLS credential allocation failed: %s",
+                       gnutls_strerror(ret));
+            goto fail;
+        }
+
+        gnutls_certificate_set_verify_function(irc_cds->tls_cred,
+                                               gnutls_cert_allow_all);
+        ret = gnutls_credentials_set(irc_cds->tls_session,
+                                     GNUTLS_CRD_CERTIFICATE, irc_cds->tls_cred);
+        if (ret != GNUTLS_E_SUCCESS) {
+            error_setg(errp, "TLS credential association failed: %s",
+                       gnutls_strerror(ret));
+            goto fail;
+        }
+
+        ret = gnutls_handshake(irc_cds->tls_session);
+        if (ret != GNUTLS_E_SUCCESS) {
+            error_setg(errp, "TLS handshake failed: %s", gnutls_strerror(ret));
+            goto fail;
+        }
+    }
+#endif
+
     sendstr = g_strdup_printf("USER qemu qemu qemu qemu\r\nNICK %s\r\n",
                               irc->nick);
     ret = irc_send(irc_cds, sendstr, strlen(sendstr));
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH for-2.4 4/5] chardev/irc: Add end-to-end encryption
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
                   ` (2 preceding siblings ...)
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 3/5] chardev/irc: Add SSL support Max Reitz
@ 2015-04-01 12:09 ` Max Reitz
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 5/5] Documentation: Document IRC char driver Max Reitz
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

IRC being usable for remote configuration, offering a secure channel is
indispensable. As can be seen from its alias "Caesar's cipher", ROT13
has been in use since ancient times and has been employed for state and
military secrets, so it is definitely well-tested, stable and secure
enough to manage a continent-spanning empire.

Its security is further proven by its ubiquity. For instance, vim
supports encryption and decryption by hfvat gur t? pbzznaq.

However, some concerns have been raised in the past whether ROT13 and
consequently all of Caesar's ciphers are actually secure for data with
non-maximum entropy. Another cipher has shown in the past how to deal
with these accusations: As DES is considered insecure, Triple DES (3DES)
has been introduced. Likewise, this patch implements Triple ROT13 in
addition to simple ROT13 to greatly improve security.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qapi-schema.json |  17 +++++++-
 qemu-char.c      | 129 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 130 insertions(+), 16 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index e4f93fd..d365352 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2876,6 +2876,20 @@
 { 'type': 'ChardevRingbuf', 'data': { '*size'  : 'int' } }
 
 ##
+# @ChardevIrcEncryption
+#
+# Enumeration of supported encryption modes over IRC.
+#
+# @none:    No encryption
+# @weak:    ROT13
+# @strong:  Triple ROT13
+#
+# Since: 2.4
+##
+{ 'enum': 'ChardevIrcEncryption',
+  'data': [ 'none', 'weak', 'strong' ] }
+
+##
 # @ChardevIrc
 #
 # Configuration info for IRC chardevs.
@@ -2889,7 +2903,8 @@
 { 'type': 'ChardevIrc', 'data': { 'addr'        : 'SocketAddress',
                                   'nick'        : 'str',
                                   'channel'     : 'str',
-                                  'ssl'         : 'bool' } }
+                                  'ssl'         : 'bool',
+                                  'encryption'  : 'ChardevIrcEncryption' } }
 
 ##
 # @ChardevBackend:
diff --git a/qemu-char.c b/qemu-char.c
index 1030d5e..1fe149d 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -30,6 +30,7 @@
 #include "qmp-commands.h"
 #include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
+#include "qapi/util.h"
 #include "qapi-visit.h"
 
 #include <unistd.h>
@@ -3321,8 +3322,78 @@ typedef struct {
     uint8_t *recvbuf;
     int recvbuf_idx;
     bool recv_line_skip;
+
+    ChardevIrcEncryption encryption;
 } IrcCharDriverState;
 
+static void caesar_cipher(uint8_t *dest, const uint8_t *src, size_t len,
+                          int shift)
+{
+    size_t i;
+
+    shift %= 26;
+    if (shift < 0) {
+        shift += 26;
+    }
+
+    for (i = 0; i < len; i++) {
+        if (isascii(src[i]) && isalpha(src[i])) {
+            dest[i] = (((src[i] & 0x1f) - 1 + shift) % 26 + 1)
+                    | (src[i] & ~0x1f);
+        } else {
+            dest[i] = src[i];
+        }
+    }
+}
+
+static void irc_encrypt(IrcCharDriverState *irc, uint8_t *buffer, size_t len)
+{
+    switch (irc->encryption) {
+    /* no encryption */
+    case CHARDEV_IRC_ENCRYPTION_NONE:
+        break;
+
+    /* ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_WEAK:
+        caesar_cipher(buffer, buffer, len, 13);
+        break;
+
+    /* Triple ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_STRONG:
+        caesar_cipher(buffer, buffer, len, 13);
+        caesar_cipher(buffer, buffer, len, 13);
+        caesar_cipher(buffer, buffer, len, 13);
+        break;
+
+    default:
+        return;
+    }
+}
+
+static void irc_decrypt(IrcCharDriverState *irc, uint8_t *buffer, size_t len)
+{
+    switch (irc->encryption) {
+    /* no encryption */
+    case CHARDEV_IRC_ENCRYPTION_NONE:
+        break;
+
+    /* ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_WEAK:
+        caesar_cipher(buffer, buffer, len, -13);
+        break;
+
+    /* Triple ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_STRONG:
+        caesar_cipher(buffer, buffer, len, -13);
+        caesar_cipher(buffer, buffer, len, -13);
+        caesar_cipher(buffer, buffer, len, -13);
+        break;
+
+    default:
+        return;
+    }
+}
+
 #ifdef CONFIG_GNUTLS
 static ssize_t irc_send(IrcCharDriverState *irc, const void *buf, size_t len)
 {
@@ -3447,6 +3518,9 @@ static int irc_chr_write(CharDriverState *s, const uint8_t *buf, int len)
         } else if (buf[i] == '\n') {
             if (irc->sendbuf) {
                 if (!irc->send_line_skip) {
+                    irc_encrypt(irc, irc->sendbuf + irc->sendbuf_prefixlen,
+                                irc->sendbuf_idx);
+
                     irc->sendbuf[irc->sendbuf_idx++] = '\r';
                     irc->sendbuf[irc->sendbuf_idx++] = '\n';
 
@@ -3486,6 +3560,9 @@ static int irc_chr_write(CharDriverState *s, const uint8_t *buf, int len)
                     irc->sendbuf[break_index] = '\r';
                     irc->sendbuf[break_index + 1] = '\n';
 
+                    irc_encrypt(irc, irc->sendbuf + irc->sendbuf_prefixlen,
+                                break_index);
+
                     irc_send(irc, irc->sendbuf, break_index + 2);
 
                     irc->sendbuf[break_index + 1] = replaced;
@@ -3573,22 +3650,27 @@ static gboolean irc_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
 
     in_query = !strcmp(real_dest, irc->nick);
 
-    /* ignore CTCP */
-    if (!strcmp(cmd, "PRIVMSG") && msg[0] != 1 &&
-        (in_query || (!strncmp(msg, irc->nick, strlen(irc->nick)) &&
-                      !isalnum(msg[strlen(irc->nick)]))))
-    {
-        const char *start = msg;
-
-        if (!in_query) {
-            start = msg + strlen(irc->nick) + 1;
-            while (*start && isspace(*start)) {
-                start++;
+    /* ignore plain-text CTCP */
+    if (!strcmp(cmd, "PRIVMSG") && msg[0] != 1) {
+        irc_decrypt(irc, (uint8_t *)msg, strlen(msg));
+
+        /* ignore encrypted CTCP as well */
+        if (in_query ? msg[0] != 1 :
+            !strncmp(msg, irc->nick, strlen(irc->nick)) &&
+            !isalnum(msg[strlen(irc->nick)]))
+        {
+            const char *start = msg;
+
+            if (!in_query) {
+                start = msg + strlen(irc->nick) + 1;
+                while (*start && isspace(*start)) {
+                    start++;
+                }
             }
-        }
 
-        qemu_chr_be_write(s, (uint8_t *)start, strlen(start));
-        qemu_chr_be_write(s, (uint8_t *)"\n", 1);
+            qemu_chr_be_write(s, (uint8_t *)start, strlen(start));
+            qemu_chr_be_write(s, (uint8_t *)"\n", 1);
+        }
     }
 
     g_free(buffer);
@@ -3985,9 +4067,11 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
 static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
-    const char *host, *port, *nick, *channel, *sockfd;
+    const char *host, *port, *nick, *channel, *sockfd, *encryption_raw;
+    ChardevIrcEncryption encryption;
     bool ssl;
     SocketAddress *addr;
+    Error *local_err = NULL;
 
     host    = qemu_opt_get(opts, "host");
     port    = qemu_opt_get(opts, "port") ?: "6667";
@@ -4000,6 +4084,15 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     ssl     = false;
 #endif
 
+    encryption_raw = qemu_opt_get(opts, "encryption") ?: "none";
+    encryption = qapi_enum_parse(ChardevIrcEncryption_lookup, encryption_raw,
+                                 CHARDEV_IRC_ENCRYPTION_MAX,
+                                 CHARDEV_IRC_ENCRYPTION_NONE, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
     if ((!host && !sockfd) || !nick || !channel) {
         error_setg(errp, "chardev: irc: Missing options");
         return;
@@ -4035,6 +4128,7 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     backend->irc->nick = g_strdup(nick);
     backend->irc->channel = g_strdup(channel);
     backend->irc->ssl = ssl;
+    backend->irc->encryption = encryption;
 }
 
 typedef struct CharDriver {
@@ -4409,6 +4503,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "ssl",
             .type = QEMU_OPT_BOOL,
 #endif
+        },{
+            .name = "encryption",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -4677,6 +4774,7 @@ static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
 
     irc_cds->recvbuf = g_malloc(1024);
     irc_cds->recvbuf_idx = 0;
+    irc_cds->encryption = CHARDEV_IRC_ENCRYPTION_NONE;
 
 #ifdef CONFIG_GNUTLS
     if (irc->ssl) {
@@ -4800,6 +4898,7 @@ static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
     }
 
     irc_cds->query = query;
+    irc_cds->encryption = irc->encryption;
 
     s = qemu_chr_alloc();
     s->chr_write = irc_chr_write;
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH for-2.4 5/5] Documentation: Document IRC char driver
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
                   ` (3 preceding siblings ...)
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 4/5] chardev/irc: Add end-to-end encryption Max Reitz
@ 2015-04-01 12:09 ` Max Reitz
  2015-04-02  8:57 ` [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add " Stefan Hajnoczi
  2015-04-02 19:19 ` Markus Armbruster
  6 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-01 12:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Kevin Wolf, Paolo Bonzini, Markus Armbruster, Max Reitz

With the IRC char driver being fully functional, it now needs to be
exposed to the broad audience. Adding documentation has been proven a
valuable step in this process.

Regarding the next steps, mouth-to-mouth propaganda will probably
suffice, considering that IRC is objectively the single best character
device qemu has to offer.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qemu-options.hx | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index 319d971..e6e0cb2 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2799,6 +2799,34 @@ or fake device.
 
 @item msmouse
 Three button serial mouse. Configure the guest to use Microsoft protocol.
+
+@item irc[,host=@var{hostname}[,port=@var{port}]][,sockfd=@var{socket_fd}],nick=@var{nick},channel=@var{channel}[,ssl=@var{ssl}][,encryption=@var{cipher}]
+Connects to the specified @var{hostname} on the given @var{port} (defaults to
+6667), using the given @var{nick}.  Alternatively, a socket file descriptor may
+be specified as @var{socket_fd}.  Once connected, the channel @var{channel} is
+joined.  A nick may be specify instead of a channel, in which case all
+communication is done through a query.
+
+TLS is enabled or disabled through the @var{ssl} option.  It is disabled by
+default.
+
+All messages are encrypted and decrypted with the selected @var{cipher}.  Valid
+options are:
+@table @option
+@item none
+No encryption
+@item weak
+ROT13
+@item strong
+Triple ROT13
+@end table
+
+Messages to QEMU must be prefixed with @var{nick} if communicating through a
+channel; when queried, no prefix must be used.  This prefix has to be encrypted
+if encryption is used.
+
+All output is emitted to @var{channel}.  There is no limitation for input
+sources, so anyone may query the QEMU instance and submit data.
 @end table
 ETEXI
 
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
                   ` (4 preceding siblings ...)
  2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 5/5] Documentation: Document IRC char driver Max Reitz
@ 2015-04-02  8:57 ` Stefan Hajnoczi
  2015-04-02 14:04   ` Max Reitz
  2015-04-02 19:19 ` Markus Armbruster
  6 siblings, 1 reply; 9+ messages in thread
From: Stefan Hajnoczi @ 2015-04-02  8:57 UTC (permalink / raw)
  To: Max Reitz; +Cc: Kevin Wolf, Paolo Bonzini, qemu-devel, Markus Armbruster

[-- Attachment #1: Type: text/plain, Size: 485 bytes --]

On Wed, Apr 01, 2015 at 02:09:12PM +0200, Max Reitz wrote:
> This series adds a long-missing IRC character device driver to qemu. See
> patch 1 for an explanation why you should have been missing it.
> 
> To make it short: IRC is a very well tested and more reliable, social,
> human-friendly, and ubiquitous[1] communication interface than any of
> the other character devices qemu has to offer.

Next year please do ELIZA:
https://en.wikipedia.org/wiki/ELIZA

:)
Stefan

[-- Attachment #2: Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver
  2015-04-02  8:57 ` [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add " Stefan Hajnoczi
@ 2015-04-02 14:04   ` Max Reitz
  0 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2015-04-02 14:04 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: Kevin Wolf, Paolo Bonzini, qemu-devel, Markus Armbruster

On 02.04.2015 10:57, Stefan Hajnoczi wrote:
> On Wed, Apr 01, 2015 at 02:09:12PM +0200, Max Reitz wrote:
>> This series adds a long-missing IRC character device driver to qemu. See
>> patch 1 for an explanation why you should have been missing it.
>>
>> To make it short: IRC is a very well tested and more reliable, social,
>> human-friendly, and ubiquitous[1] communication interface than any of
>> the other character devices qemu has to offer.
> Next year please do ELIZA:
> https://en.wikipedia.org/wiki/ELIZA

Why not complete Emacs integration? ;-)

Max

> :)
> Stefan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver
  2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
                   ` (5 preceding siblings ...)
  2015-04-02  8:57 ` [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add " Stefan Hajnoczi
@ 2015-04-02 19:19 ` Markus Armbruster
  6 siblings, 0 replies; 9+ messages in thread
From: Markus Armbruster @ 2015-04-02 19:19 UTC (permalink / raw)
  To: Max Reitz; +Cc: Kevin Wolf, Paolo Bonzini, qemu-devel

I'd go for a Tested-by if was social enough to let #qemu jabber to my
monitor.  Maybe one of our younger, facebook-hardened contributors?

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2015-04-02 19:19 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 1/5] " Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 2/5] chardev/irc: Add sockfd option Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 3/5] chardev/irc: Add SSL support Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 4/5] chardev/irc: Add end-to-end encryption Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 5/5] Documentation: Document IRC char driver Max Reitz
2015-04-02  8:57 ` [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add " Stefan Hajnoczi
2015-04-02 14:04   ` Max Reitz
2015-04-02 19:19 ` Markus Armbruster

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.