diff mbox series

[v2,7/9] qmp: teach 'getfd' to import sockets on win32

Message ID 20230129182414.583349-8-marcandre.lureau@redhat.com
State New
Headers show
Series Various win32 fixes & teach 'getfd' QMP command to import sockets | expand

Commit Message

Marc-André Lureau Jan. 29, 2023, 6:24 p.m. UTC
From: Marc-André Lureau <marcandre.lureau@redhat.com>

A process with enough capabilities can duplicate a socket to QEMU.
Modify 'getfd' to import it and add it to the monitor fd list, so it can
be later used by other commands.

Note that we actually store the SOCKET in the FD list, appropriate care
must now be taken to use the correct socket functions (similar approach
is taken by our io/ code and in glib, this is internal and shouldn't
affect the QEMU/QMP users)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi/misc.json     | 16 ++++++++--
 monitor/hmp-cmds.c |  6 +++-
 monitor/misc.c     | 75 ++++++++++++++++++++++++++++++++++++----------
 3 files changed, 79 insertions(+), 18 deletions(-)
diff mbox series

Patch

diff --git a/qapi/misc.json b/qapi/misc.json
index 27ef5a2b20..cd36d8befb 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -249,10 +249,18 @@ 
 ##
 # @getfd:
 #
-# Receive a file descriptor via SCM rights and assign it a name
+# On UNIX, receive a file descriptor via SCM rights and assign it a name.
+#
+# On Windows, (where ancillary socket fd-passing isn't an option yet), add a
+# socket that was duplicated to QEMU process with WSADuplicateSocketW() via
+# WSASocket() & WSAPROTOCOL_INFOW structure and assign it a name. A SOCKET is
+# considered as a kind of "file descriptor" in QMP context, for historical
+# reasons and simplicity. QEMU takes care to use socket functions appropriately.
 #
 # @fdname: file descriptor name
 #
+# @wsa-info: a WSAPROTOCOL_INFOW structure (encoded in base64). Since 8.0.
+#
 # Returns: Nothing on success
 #
 # Since: 0.14
@@ -270,7 +278,11 @@ 
 # <- { "return": {} }
 #
 ##
-{ 'command': 'getfd', 'data': {'fdname': 'str'} }
+{ 'command': 'getfd', 'data': {
+    'fdname': 'str',
+    '*wsa-info': {'type': 'str', 'if': 'CONFIG_WIN32'}
+  }
+}
 
 ##
 # @closefd:
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 1dba973092..fc9145b8fa 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1232,7 +1232,11 @@  void hmp_getfd(Monitor *mon, const QDict *qdict)
     const char *fdname = qdict_get_str(qdict, "fdname");
     Error *err = NULL;
 
-    qmp_getfd(fdname, &err);
+    qmp_getfd(fdname,
+#ifdef WIN32
+              NULL,
+#endif
+              &err);
     hmp_handle_error(mon, err);
 }
 
diff --git a/monitor/misc.c b/monitor/misc.c
index 053af4045e..96c4977e5a 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -71,6 +71,7 @@ 
 #include "qapi/error.h"
 #include "qapi/qmp-event.h"
 #include "qemu/cutils.h"
+#include "qemu/sockets.h"
 
 #if defined(TARGET_S390X)
 #include "hw/s390x/storage-keys.h"
@@ -957,27 +958,29 @@  static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
     QLIST_INSERT_HEAD (&capture_head, s, entries);
 }
 
-void qmp_getfd(const char *fdname, Error **errp)
+static void close_fd(int fd)
 {
-    Monitor *cur_mon = monitor_cur();
-    mon_fd_t *monfd;
-    int fd, tmp_fd;
-
-    fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
-    if (fd == -1) {
-        error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
-        return;
+    if (fd_is_socket(fd)) {
+        closesocket(fd);
+    } else {
+        close(fd);
     }
+}
+
+static void monitor_add_fd(Monitor *mon, int fd, const char *fdname, Error **errp)
+{
+    mon_fd_t *monfd;
+    int tmp_fd;
 
     if (qemu_isdigit(fdname[0])) {
-        close(fd);
+        close_fd(fd);
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
                    "a name not starting with a digit");
         return;
     }
 
-    QEMU_LOCK_GUARD(&cur_mon->mon_lock);
-    QLIST_FOREACH(monfd, &cur_mon->fds, next) {
+    QEMU_LOCK_GUARD(&mon->mon_lock);
+    QLIST_FOREACH(monfd, &mon->fds, next) {
         if (strcmp(monfd->name, fdname) != 0) {
             continue;
         }
@@ -985,7 +988,7 @@  void qmp_getfd(const char *fdname, Error **errp)
         tmp_fd = monfd->fd;
         monfd->fd = fd;
         /* Make sure close() is outside critical section */
-        close(tmp_fd);
+        close_fd(tmp_fd);
         return;
     }
 
@@ -993,7 +996,49 @@  void qmp_getfd(const char *fdname, Error **errp)
     monfd->name = g_strdup(fdname);
     monfd->fd = fd;
 
-    QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
+    QLIST_INSERT_HEAD(&mon->fds, monfd, next);
+}
+
+void qmp_getfd(const char *fdname,
+#ifdef WIN32
+               const char *wsa_info,
+#endif
+               Error **errp)
+{
+    Monitor *cur_mon = monitor_cur();
+    int fd;
+
+#ifdef WIN32
+    if (wsa_info) {
+        g_autofree WSAPROTOCOL_INFOW *info = NULL;
+        gsize len;
+        SOCKET sk;
+
+        info = (void *)g_base64_decode(wsa_info, &len);
+        if (len != sizeof(*info)) {
+            error_setg(errp, "Invalid WSAPROTOCOL_INFOW value");
+            return;
+        }
+
+        sk = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+                        FROM_PROTOCOL_INFO, info, 0, 0);
+        if (sk == INVALID_SOCKET) {
+            g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError());
+            error_setg(errp, "Couldn't create socket: %s", emsg);
+            return;
+        }
+
+        return monitor_add_fd(cur_mon, sk, fdname, errp);
+    }
+#endif
+
+    fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
+    if (fd == -1) {
+        error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
+        return;
+    }
+
+    return monitor_add_fd(cur_mon, fd, fdname, errp);
 }
 
 void qmp_closefd(const char *fdname, Error **errp)
@@ -1014,7 +1059,7 @@  void qmp_closefd(const char *fdname, Error **errp)
         g_free(monfd);
         qemu_mutex_unlock(&cur_mon->mon_lock);
         /* Make sure close() is outside critical section */
-        close(tmp_fd);
+        close_fd(tmp_fd);
         return;
     }