diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assuan-defs.h | 2 | ||||
-rw-r--r-- | src/assuan-handler.c | 44 | ||||
-rw-r--r-- | src/assuan-listen.c | 2 | ||||
-rw-r--r-- | src/assuan-pipe-connect.c | 42 | ||||
-rw-r--r-- | src/assuan-pipe-server.c | 11 | ||||
-rw-r--r-- | src/assuan-socket-connect.c | 26 | ||||
-rw-r--r-- | src/assuan-socket-server.c | 4 | ||||
-rw-r--r-- | src/client.c | 3 | ||||
-rw-r--r-- | src/system-w32.c | 186 |
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); |