aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assuan-defs.h2
-rw-r--r--src/assuan-handler.c44
-rw-r--r--src/assuan-listen.c2
-rw-r--r--src/assuan-pipe-connect.c42
-rw-r--r--src/assuan-pipe-server.c11
-rw-r--r--src/assuan-socket-connect.c26
-rw-r--r--src/assuan-socket-server.c4
-rw-r--r--src/client.c3
-rw-r--r--src/system-w32.c186
9 files changed, 304 insertions, 16 deletions
diff --git a/src/assuan-defs.h b/src/assuan-defs.h
index 47ff4ef..68bd983 100644
--- a/src/assuan-defs.h
+++ b/src/assuan-defs.h
@@ -327,6 +327,8 @@ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err);
#ifdef HAVE_W32_SYSTEM
char *_assuan_w32_strerror (assuan_context_t ctx, int ec);
+gpg_error_t w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd);
+gpg_error_t w32_fdpass_recv (assuan_context_t ctx, assuan_fd_t *fd);
#endif /*HAVE_W32_SYSTEM*/
diff --git a/src/assuan-handler.c b/src/assuan-handler.c
index 126eccb..8eae253 100644
--- a/src/assuan-handler.c
+++ b/src/assuan-handler.c
@@ -27,6 +27,9 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#if HAVE_W32_SYSTEM || HAVE_W64_SYSTEM
+#include <fcntl.h>
+#endif
#include "assuan-defs.h"
#include "debug.h"
@@ -356,6 +359,44 @@ std_handler_output (assuan_context_t ctx, char *line)
}
+#if HAVE_W32_SYSTEM || HAVE_W64_SYSTEM
+static const char w32_help_sendfd[] =
+ "SENDFD <N>\n"
+ "\n"
+ "Used by a client to pass a file HANDLE to the server.\n"
+ "The server opens <N> as a local file HANDLE.";
+static gpg_error_t
+w32_handler_sendfd (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err = 0;
+ char *endp;
+ intptr_t file_handle;
+ int fd;
+
+#if HAVE_W64_SYSTEM
+ file_handle = strtoull (line, &endp, 16);
+#elif HAVE_W32_SYSTEM
+ file_handle = strtoul (line, &endp, 16);
+#endif
+
+ if (*endp)
+ {
+ err = set_error (ctx, GPG_ERR_ASS_SYNTAX, "hex number required");
+ return PROCESS_DONE (ctx, err);
+ }
+
+ fd = _open_osfhandle ((intptr_t)file_handle, _O_RDWR);
+ if (fd < 0)
+ {
+ CloseHandle ((HANDLE)file_handle);
+ err = GPG_ERR_ASSUAN;
+ }
+
+ ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = (assuan_fd_t)fd;
+ return PROCESS_DONE (ctx, err);
+}
+#endif
+
/* This is a table with the standard commands and handler for them.
The table is used to initialize a new context and associate strings
with default handlers */
@@ -376,6 +417,9 @@ static struct {
{ "INPUT", std_handler_input, std_help_input, 0 },
{ "OUTPUT", std_handler_output, std_help_output, 0 },
+#if HAVE_W32_SYSTEM
+ { "SENDFD", w32_handler_sendfd, w32_help_sendfd, 1 },
+#endif
{ } };
diff --git a/src/assuan-listen.c b/src/assuan-listen.c
index 6755d59..93560ff 100644
--- a/src/assuan-listen.c
+++ b/src/assuan-listen.c
@@ -117,7 +117,7 @@ assuan_accept (assuan_context_t ctx)
else
{
static char const okstr[] = "OK Pleased to meet you";
- pid_t apid = assuan_get_pid (ctx);
+ pid_t apid = getpid ();
if (apid != ASSUAN_INVALID_PID)
{
char tmpbuf[50];
diff --git a/src/assuan-pipe-connect.c b/src/assuan-pipe-connect.c
index 1589d79..fc56334 100644
--- a/src/assuan-pipe-connect.c
+++ b/src/assuan-pipe-connect.c
@@ -104,7 +104,31 @@ initial_handshake (assuan_context_t ctx)
if (err)
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
"can't connect server: %s", gpg_strerror (err));
- else if (response != ASSUAN_RESPONSE_OK)
+ else if (response == ASSUAN_RESPONSE_OK)
+ {
+#ifdef HAVE_W32_SYSTEM
+ const char *line = ctx->inbound.line + off;
+ int pid = ASSUAN_INVALID_PID;
+
+ /* Parse the message: OK ..., process %i */
+ line = strchr (line, ',');
+ if (line)
+ {
+ line = strchr (line + 1, ' ');
+ if (line)
+ {
+ line = strchr (line + 1, ' ');
+ if (line)
+ pid = atoi (line + 1);
+ }
+ }
+ if (pid != ASSUAN_INVALID_PID)
+ ctx->pid = pid;
+#else
+ ;
+#endif
+ }
+ else
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
"can't connect server: `%s'", ctx->inbound.line);
@@ -209,7 +233,11 @@ pipe_connect (assuan_context_t ctx,
ctx->engine.release = _assuan_client_release;
ctx->engine.readfnc = _assuan_simple_read;
ctx->engine.writefnc = _assuan_simple_write;
+#ifdef HAVE_W32_SYSTEM
+ ctx->engine.sendfd = w32_fdpass_send;
+#else
ctx->engine.sendfd = NULL;
+#endif
ctx->engine.receivefd = NULL;
ctx->finish_handler = _assuan_client_finish;
ctx->max_accepts = 1;
@@ -413,16 +441,12 @@ assuan_pipe_connect (assuan_context_t ctx,
TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_pipe_connect", ctx,
"name=%s, flags=0x%x", name ? name : "(null)", flags);
+#ifndef HAVE_W32_SYSTEM
if (flags & ASSUAN_PIPE_CONNECT_FDPASSING)
- {
-#ifdef HAVE_W32_SYSTEM
- return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
-#else
- return socketpair_connect (ctx, name, argv, fd_child_list,
- atfork, atforkvalue);
-#endif
- }
+ return socketpair_connect (ctx, name, argv, fd_child_list,
+ atfork, atforkvalue);
else
+#endif
return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue,
flags);
}
diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c
index db66978..7977b83 100644
--- a/src/assuan-pipe-server.c
+++ b/src/assuan-pipe-server.c
@@ -82,8 +82,11 @@ assuan_init_pipe_server (assuan_context_t ctx, assuan_fd_t filedes[2])
return TRACE_ERR (rc);
#ifdef HAVE_W32_SYSTEM
- infd = filedes[0];
- outfd = filedes[1];
+ if (filedes)
+ {
+ infd = filedes[0];
+ outfd = filedes[1];
+ }
#else
s = getenv ("_assuan_connection_fd");
if (s && *s && is_valid_socket (s))
@@ -115,7 +118,11 @@ assuan_init_pipe_server (assuan_context_t ctx, assuan_fd_t filedes[2])
ctx->engine.readfnc = _assuan_simple_read;
ctx->engine.writefnc = _assuan_simple_write;
ctx->engine.sendfd = NULL;
+#ifdef HAVE_W32_SYSTEM
+ ctx->engine.receivefd = w32_fdpass_recv;
+#else
ctx->engine.receivefd = NULL;
+#endif
ctx->max_accepts = 1;
s = getenv ("_assuan_pipe_connect_pid");
diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c
index 019a41c..457edfe 100644
--- a/src/assuan-socket-connect.c
+++ b/src/assuan-socket-connect.c
@@ -115,8 +115,12 @@ _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd,
ctx->max_accepts = -1;
ctx->flags.is_socket = 1;
+#ifdef HAVE_W32_SYSTEM
+ ctx->engine.sendfd = w32_fdpass_send;
+#else
if (flags & ASSUAN_SOCKET_CONNECT_FDPASSING)
_assuan_init_uds_io (ctx);
+#endif
/* initial handshake */
{
@@ -127,7 +131,27 @@ _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd,
if (err)
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
"can't connect to server: %s\n", gpg_strerror (err));
- else if (response != ASSUAN_RESPONSE_OK)
+ else if (response == ASSUAN_RESPONSE_OK)
+ {
+ const char *line = ctx->inbound.line + off;
+ int pid = ASSUAN_INVALID_PID;
+
+ /* Parse the message: OK ..., process %i */
+ line = strchr (line, ',');
+ if (line)
+ {
+ line = strchr (line + 1, ' ');
+ if (line)
+ {
+ line = strchr (line + 1, ' ');
+ if (line)
+ pid = atoi (line + 1);
+ }
+ }
+ if (pid != ASSUAN_INVALID_PID)
+ ctx->pid = pid;
+ }
+ else
{
char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line);
if (sname)
diff --git a/src/assuan-socket-server.c b/src/assuan-socket-server.c
index a8330a8..f5fe038 100644
--- a/src/assuan-socket-server.c
+++ b/src/assuan-socket-server.c
@@ -238,8 +238,12 @@ assuan_init_socket_server (assuan_context_t ctx, assuan_fd_t fd,
: accept_connection);
ctx->finish_handler = _assuan_server_finish;
+#ifdef HAVE_W32_SYSTEM
+ ctx->engine.receivefd = w32_fdpass_recv;
+#else
if (flags & ASSUAN_SOCKET_SERVER_FDPASSING)
_assuan_init_uds_io (ctx);
+#endif
rc = _assuan_register_std_commands (ctx);
if (rc)
diff --git a/src/client.c b/src/client.c
index e0759f6..1746788 100644
--- a/src/client.c
+++ b/src/client.c
@@ -50,7 +50,8 @@ _assuan_client_finish (assuan_context_t ctx)
}
if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid)
{
- _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0);
+ if (!ctx->flags.is_socket)
+ _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0);
ctx->pid = ASSUAN_INVALID_PID;
}
diff --git a/src/system-w32.c b/src/system-w32.c
index cfc1d61..dfd2ba2 100644
--- a/src/system-w32.c
+++ b/src/system-w32.c
@@ -167,6 +167,144 @@ __assuan_close (assuan_context_t ctx, assuan_fd_t fd)
+/* To encode/decode file HANDLE, we use FDPASS_FORMAT */
+#define FDPASS_FORMAT "%p"
+#define FDPASS_MSG_SIZE (sizeof (uintptr_t)*2 + 1)
+
+static gpg_error_t
+get_file_handle (int fd, int server_pid, HANDLE *r_handle)
+{
+ HANDLE prochandle, handle, newhandle;
+
+ handle = (void *)_get_osfhandle (fd);
+
+ prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, server_pid);
+ if (!prochandle)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
+
+ if (!DuplicateHandle (GetCurrentProcess (), handle, prochandle, &newhandle,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle (prochandle);
+ return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
+ }
+ CloseHandle (prochandle);
+ *r_handle = newhandle;
+ return 0;
+}
+
+
+/*
+ * On Windows, we can consider two different ways about what FD means:
+ *
+ * (1) POSIX fd
+ * (2) Windows file HANDLE
+ *
+ * In assuan, we use assuan_fd_t for socket/pipe.
+ *
+ * Here, it refers file object.
+ *
+ * If we started the design and implementation now, it would be more
+ * natural (easy to understand, no confusion) to use the "int" type
+ * and POSIX fd here. Considering the purpose of sending fd (the file
+ * object to share), it is better portability-wise, even though use of
+ * POSIX fd on Windows requires emulation layer. (These days, in
+ * GnuPG, we use gpgrt's estream for file assces and gpgrt_fileno for
+ * POSIX fd.)
+ *
+ * That is:
+ *
+ * - assuan_sendfd/assuan_recvfd sends/receives POSIX fd
+ * - assuan_get_input_fd/assuan_get_output_fd returns POSIX fd
+ *
+ * However, those APIs now uses assuan_fd_t. That's troublesome or it
+ * allows confusion about the semantics of the APIs.
+ *
+ * Perhaps, avoiding API/ABI breaks, we would need introducing new APIs:
+ *
+ * - assuan_sendFD/assuan_recvFD sends/receives POSIX fd
+ * - assuan_get_input_FD/assuan_get_output_FD returns POSIX fd
+ *
+ * For this experiment, we don't care about API/ABI breaks, for now.
+ * And use POSIX fd here.
+ *
+ * We use sending MSG_OOB, but it only allows a single-byte in TCP.
+ * So, it is used to notify other end for fdpassing.
+ */
+gpg_error_t
+w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd)
+{
+ char fdpass_msg[256];
+ int res;
+ int fd0; /* POSIX fd */
+ intptr_t fd_converted_to_integer;
+ HANDLE file_handle;
+ gpg_error_t err;
+
+ fd_converted_to_integer = (intptr_t)fd;
+ fd0 = (int)fd_converted_to_integer; /* Bit pattern is possibly truncated. */
+
+ err = get_file_handle (fd0, ctx->pid, &file_handle);
+ if (err)
+ return err;
+
+ res = snprintf (fdpass_msg, sizeof (fdpass_msg), "SENDFD %p", file_handle);
+ if (res < 0)
+ {
+ CloseHandle (file_handle);
+ return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
+ }
+
+ err = assuan_transact (ctx, fdpass_msg, NULL, NULL, NULL, NULL, NULL, NULL);
+ return err;
+}
+
+static int
+process_fdpass_msg (const char *fdpass_msg, size_t msglen, int *r_fd)
+{
+ void *file_handle;
+ int res;
+ int fd;
+
+ *r_fd = -1;
+
+ res = sscanf (fdpass_msg, FDPASS_FORMAT, &file_handle);
+ if (res != 1)
+ return -1;
+
+ fd = _open_osfhandle ((intptr_t)file_handle, _O_RDWR);
+ if (fd < 0)
+ {
+ CloseHandle (file_handle);
+ return -1;
+ }
+
+ *r_fd = fd;
+ return 0;
+}
+
+gpg_error_t
+w32_fdpass_recv (assuan_context_t ctx, assuan_fd_t *fd)
+{
+ int i;
+
+ if (!ctx->uds.pendingfdscount)
+ {
+ TRACE0 (ctx, ASSUAN_LOG_SYSIO, "w32_receivefd", ctx,
+ "no pending file descriptors");
+ return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
+ }
+
+ *fd = ctx->uds.pendingfds[0];
+ for (i=1; i < ctx->uds.pendingfdscount; i++)
+ ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
+ ctx->uds.pendingfdscount--;
+
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "w32_fdpass_recv", ctx,
+ "received fd: %p", ctx->uds.pendingfds[0]);
+ return 0;
+}
+
ssize_t
__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
{
@@ -175,7 +313,53 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
if (ctx->flags.is_socket)
{
+ fd_set fds;
int tries = 3;
+ fd_set efds;
+
+ FD_ZERO (&fds);
+ FD_SET (HANDLE2SOCKET (fd), &fds);
+ FD_ZERO (&efds);
+ FD_SET (HANDLE2SOCKET (fd), &efds);
+ res = select (0, &fds, NULL, &efds, NULL);
+ if (res < 0)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+ else if (FD_ISSET (HANDLE2SOCKET (fd), &efds))
+ {
+ int fd_recv;
+ char fdpass_msg[FDPASS_MSG_SIZE];
+
+ /* the message of ! */
+ res = recv (HANDLE2SOCKET (fd), fdpass_msg, sizeof (fdpass_msg), MSG_OOB);
+ if (res < 0)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+
+ /* the body of message */
+ res = recv (HANDLE2SOCKET (fd), fdpass_msg, sizeof (fdpass_msg), 0);
+ if (res < 0)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+
+ res = process_fdpass_msg (fdpass_msg, res, &fd_recv);
+ if (res < 0)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+
+ ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = (assuan_fd_t)fd_recv;
+ TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx,
+ "received fd: %d", fd_recv);
+ /* Fall through */
+ }
again:
ec = 0;
@@ -189,8 +373,6 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
layer then needs to take care of EAGAIN. No need to
specify a timeout - the socket is not expected to be in
blocking mode. */
- fd_set fds;
-
FD_ZERO (&fds);
FD_SET (HANDLE2SOCKET (fd), &fds);
select (0, &fds, NULL, NULL, NULL);