diff options
-rw-r--r-- | src/assuan-listen.c | 2 | ||||
-rw-r--r-- | src/assuan-pipe-connect.c | 26 | ||||
-rw-r--r-- | src/assuan-pipe-server.c | 7 | ||||
-rw-r--r-- | src/assuan-socket-connect.c | 26 | ||||
-rw-r--r-- | src/assuan-socket-server.c | 4 | ||||
-rw-r--r-- | src/client.c | 2 | ||||
-rw-r--r-- | src/system-w32.c | 76 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/fdpassing-socket.c | 359 | ||||
-rw-r--r-- | tests/fdpassing.c | 24 |
10 files changed, 510 insertions, 20 deletions
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 13ea3de..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); diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c index 8da54ad..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)) 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 dcb2a1a..1746788 100644 --- a/src/client.c +++ b/src/client.c @@ -51,7 +51,7 @@ _assuan_client_finish (assuan_context_t ctx) if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid) { if (!ctx->flags.is_socket) - _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0); + _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 76ff2a0..1392a1e 100644 --- a/src/system-w32.c +++ b/src/system-w32.c @@ -167,6 +167,10 @@ __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) + /* Get a file HANDLE to send, from POSIX fd. */ static gpg_error_t get_file_handle (int fd, int server_pid, HANDLE *r_handle) @@ -220,6 +224,30 @@ w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd) 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; +} + /* Receive a HANDLE from the peer and turn it into a FD (POSIX fd). */ gpg_error_t @@ -252,7 +280,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; @@ -266,8 +340,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); diff --git a/tests/Makefile.am b/tests/Makefile.am index f43c712..f61cec5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -29,6 +29,7 @@ TESTS = version if HAVE_W32_SYSTEM testtools = +TESTS += fdpassing else TESTS += pipeconnect testtools = socks5 @@ -43,5 +44,4 @@ AM_LDFLAGS = -no-install noinst_HEADERS = common.h noinst_PROGRAMS = $(TESTS) $(w32cetools) $(testtools) -LDADD = ../src/libassuan.la $(GPG_ERROR_LIBS) \ - @LDADD_FOR_TESTS_KLUDGE@ +LDADD = ../src/libassuan.la $(GPG_ERROR_LIBS) @LDADD_FOR_TESTS_KLUDGE@ diff --git a/tests/fdpassing-socket.c b/tests/fdpassing-socket.c new file mode 100644 index 0000000..bbf4b97 --- /dev/null +++ b/tests/fdpassing-socket.c @@ -0,0 +1,359 @@ +/* fdpassing - Check the file descriptor passing. + Copyright (C) 2006, 2009 Free Software Foundation, Inc. + + This file is part of Assuan. + + Assuan is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + Assuan is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <sys/stat.h> +#if HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# include <wincrypt.h> +# include <io.h> +#else +# include <sys/socket.h> +#endif +#include <unistd.h> +#include <errno.h> + +#include "../src/assuan.h" +#include "common.h" + + +/* + + S E R V E R + +*/ + +static gpg_error_t +cmd_echo (assuan_context_t ctx, char *line) +{ + assuan_fd_t fd; + int c; + estream_t fp; + int nbytes; + + log_info ("got ECHO command (%s)\n", line); + + fd = assuan_get_input_fd (ctx); + if (fd == ASSUAN_INVALID_FD) + return gpg_error (GPG_ERR_ASS_NO_INPUT); + fp = gpgrt_fdopen ((int)fd, "r"); + if (!fp) + { + log_error ("fdopen failed on input fd: %s\n", strerror (errno)); + return gpg_error (GPG_ERR_ASS_GENERAL); + } + nbytes = 0; + while ( (c = gpgrt_fgetc (fp)) != -1) + { + putc (c, stdout); + nbytes++; + } + fflush (stdout); + log_info ("done printing %d bytes to stdout\n", nbytes); + + gpgrt_fclose (fp); + return 0; +} + +static gpg_error_t +register_commands (assuan_context_t ctx) +{ + static struct + { + const char *name; + gpg_error_t (*handler) (assuan_context_t, char *line); + } table[] = + { + { "ECHO", cmd_echo }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { NULL, NULL } + }; + int i; + gpg_error_t rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); + if (rc) + return rc; + } + return 0; +} + + +#define SUN_LEN(ptr) ((size_t) (( \ + + strlen ((ptr)->sun_path)) + +static assuan_sock_nonce_t socket_nonce; + +static void +server (const char *socketname) +{ + int rc; + assuan_context_t ctx; + assuan_fd_t fd; + struct sockaddr_un unaddr_struct; + struct sockaddr *addr; + socklen_t len; + + log_info ("server started\n"); + + fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0); + if (fd == ASSUAN_INVALID_FD) + log_fatal ("assuan_sock_new failed\n"); + + addr = (struct sockaddr *)&unaddr_struct; + rc = assuan_sock_set_sockaddr_un (socketname, addr, NULL); + if (rc) + { + assuan_sock_close (fd); + log_fatal ("assuan_sock_set_sockaddr_un failed: %s\n", gpg_strerror (rc)); + } + + len = offsetof (struct sockaddr_un, sun_path) + + strlen (unaddr_struct.sun_path); + rc = assuan_sock_bind (fd, addr, len); + if (rc) + { + assuan_sock_close (fd); + log_fatal ("assuan_sock_bind failed: %s\n", gpg_strerror (rc)); + } + + rc = assuan_sock_get_nonce (addr, len, &socket_nonce); + if (rc) + { + assuan_sock_close (fd); + log_fatal ("assuan_sock_get_nonce failed: %s\n", gpg_strerror (rc)); + } + + rc = listen (HANDLE2SOCKET (fd), 5); + if (rc < 0) + { + assuan_sock_close (fd); + log_fatal ("listen failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + } + + rc = assuan_new (&ctx); + if (rc) + log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); + + rc = assuan_init_socket_server (ctx, fd, 0); + if (rc) + log_fatal ("assuan_init_socket_server failed: %s\n", gpg_strerror (rc)); + + assuan_set_sock_nonce (ctx, &socket_nonce); + + rc = register_commands (ctx); + if (rc) + log_fatal ("register_commands failed: %s\n", gpg_strerror(rc)); + + assuan_set_log_stream (ctx, stderr); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc) + { + if (rc != -1) + log_error ("assuan_accept failed: %s\n", gpg_strerror (rc)); + break; + } + + log_info ("client connected. Client's pid is %ld\n", + (long)assuan_get_pid (ctx)); + + rc = assuan_process (ctx); + if (rc) + log_error ("assuan_process failed: %s\n", gpg_strerror (rc)); + } + + assuan_release (ctx); +} + + + + +/* + + C L I E N T + +*/ + + +/* Client main. If true is returned, a disconnect has not been done. */ +static int +client (assuan_context_t ctx, const char *fname) +{ + int rc; + estream_t fp; + int i; + + log_info ("client started. Servers's pid is %ld\n", + (long)assuan_get_pid (ctx)); + + for (i=0; i < 6; i++) + { + fp = gpgrt_fopen (fname, "r"); + if (!fp) + { + log_error ("failed to open `%s': %s\n", fname, + strerror (errno)); + return -1; + } + + rc = assuan_sendfd (ctx, (assuan_fd_t)gpgrt_fileno (fp)); + if (rc) + { + gpgrt_fclose (fp); + log_error ("assuan_sendfd failed: %s\n", gpg_strerror (rc)); + return -1; + } + gpgrt_fclose (fp); + + rc = assuan_transact (ctx, "INPUT FD", NULL, NULL, NULL, NULL, + NULL, NULL); + if (rc) + { + log_error ("sending INPUT FD failed: %s\n", gpg_strerror (rc)); + return -1; + } + + rc = assuan_transact (ctx, "ECHO", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error ("sending ECHO failed: %s\n", gpg_strerror (rc)); + return -1; + } + } + + /* Give us some time to check with lsof that all descriptors are closed. */ +/* sleep (10); */ + + assuan_release (ctx); + return 0; +} + + + + +/* + + M A I N + +*/ +int +main (int argc, char **argv) +{ + int last_argc = -1; + assuan_context_t ctx; + gpg_error_t err; + int is_server = 0; + char *fname = prepend_srcdir ("motd"); + const char *socketname = NULL; + + if (argc) + { + log_set_prefix (*argv); + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--help")) + { + puts ( +"usage: ./fdpassing [options]\n" +"\n" +"Options:\n" +" --verbose Show what is going on\n" +" --socketname Specify the socket path.\n" +); + exit (0); + } + if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--server")) + { + is_server = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--socketname")) + { + argc--; argv++; + if (argc) + { + socketname = *argv++; + argc--; + } + } + } + + + assuan_sock_init (); + assuan_set_assuan_log_prefix (log_prefix); + + if (is_server) + { + server (socketname); + log_info ("server finished\n"); + } + else + { + err = assuan_new (&ctx); + if (err) + log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); + + err = assuan_socket_connect (ctx, socketname, 0, 0); + if (err) + { + log_error ("assuan_socket_connect failed: %s\n", gpg_strerror (err)); + assuan_release (ctx); + errorcount++; + } + else + { + if (client (ctx, fname)) + { + log_info ("waiting for server to terminate...\n"); + assuan_release (ctx); + } + log_info ("client finished\n"); + } + } + + xfree (fname); + return errorcount ? 1 : 0; +} diff --git a/tests/fdpassing.c b/tests/fdpassing.c index 0e23ffc..6323a41 100644 --- a/tests/fdpassing.c +++ b/tests/fdpassing.c @@ -25,10 +25,8 @@ #include <string.h> #include <assert.h> #include <sys/stat.h> -#include <sys/socket.h> #include <unistd.h> #include <errno.h> -#include <sys/wait.h> /* Used by main driver. */ #include "../src/assuan.h" #include "common.h" @@ -62,11 +60,11 @@ cmd_echo (assuan_context_t ctx, char *line) nbytes = 0; while ( (c=getc (fp)) != -1) { - putc (c, stdout); + putc (c, stderr); nbytes++; } - fflush (stdout); - log_info ("done printing %d bytes to stdout\n", nbytes); + fflush (stderr); + log_info ("done printing %d bytes to stderr\n", nbytes); fclose (fp); return 0; @@ -104,14 +102,18 @@ server (void) { int rc; assuan_context_t ctx; + assuan_fd_t filedes[2]; log_info ("server started\n"); + filedes[0] = _get_osfhandle (0); + filedes[1] = _get_osfhandle (1); + rc = assuan_new (&ctx); if (rc) log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); - rc = assuan_init_pipe_server (ctx, NULL); + rc = assuan_init_pipe_server (ctx, filedes); if (rc) log_fatal ("assuan_init_pipe_server failed: %s\n", gpg_strerror (rc)); @@ -119,7 +121,7 @@ server (void) if (rc) log_fatal ("register_commands failed: %s\n", gpg_strerror(rc)); - assuan_set_log_stream (ctx, stderr); + // assuan_set_log_stream (ctx, stderr); for (;;) { @@ -278,11 +280,11 @@ main (int argc, char **argv) { const char *loc; - no_close_fds[0] = 2; + no_close_fds[0] = _get_osfhandle (fileno (stderr)); no_close_fds[1] = -1; if (with_exec) { - arglist[0] = "fdpassing"; + arglist[0] = "fdpassing.exe"; arglist[1] = "--server"; arglist[2] = verbose? "--verbose":NULL; arglist[3] = NULL; @@ -292,7 +294,9 @@ main (int argc, char **argv) if (err) log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); - err = assuan_pipe_connect (ctx, with_exec? "./fdpassing":NULL, + assuan_set_log_stream (ctx, stderr); + + err = assuan_pipe_connect (ctx, with_exec? "./fdpassing.exe":NULL, with_exec ? arglist : &loc, no_close_fds, NULL, NULL, 1); if (err) |