diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2022-10-14 11:42:55 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2022-10-14 11:42:55 +0900 |
commit | f0b179a0a71bd6d37300a542a517c837dcd3d386 (patch) | |
tree | 94704b63d28effdcd0705edf246dce5d9bf73d70 | |
parent | 0b9f0a7cdb815c6447008f7530e6510b86ae7cf1 (diff) | |
download | libassuan-f0b179a0a71bd6d37300a542a517c837dcd3d386.tar.gz |
experiment: Implement receving fd for Windows, part 4/N.
* src/assuan-defs.h (w32_fdpass_recv): New.
* src/assuan-socket-server.c (assuan_init_socket_server): Use it.
* src/system-w32.c (get_file_handle): New.
(w32_fdpass_send): Implement.
(process_fdpass_msg): Implement.
(w32_fdpass_recv): New.
(__assuan_read): De-multiplex the sendfd message and normal message.
--
GnuPG-bug-id: 6236
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
-rw-r--r-- | src/assuan-defs.h | 1 | ||||
-rw-r--r-- | src/assuan-socket-server.c | 4 | ||||
-rw-r--r-- | src/system-w32.c | 197 |
3 files changed, 168 insertions, 34 deletions
diff --git a/src/assuan-defs.h b/src/assuan-defs.h index de2a6d4..68bd983 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -328,6 +328,7 @@ 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-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/system-w32.c b/src/system-w32.c index 9694481..f1f2716 100644 --- a/src/system-w32.c +++ b/src/system-w32.c @@ -167,27 +167,142 @@ __assuan_close (assuan_context_t ctx, assuan_fd_t fd) +/* To encode/decode file HANDLE, we use FDPASS_FORMAT */ +#define FDPASS_FORMAT "%p" + +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]; size_t msglen; 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), FDPASS_FORMAT, file_handle); + if (res < 0) + { + CloseHandle (file_handle); + return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/ + } - /* not yet implemented. */ - (void)fd; - msglen = 6; - strcpy (fdpass_msg, "hello!"); - res = send (HANDLE2SOCKET (ctx->outbound.fd), fdpass_msg, msglen, MSG_OOB); + msglen = (size_t)res + 1; /* Including NUL. */ + + res = send (HANDLE2SOCKET (ctx->outbound.fd), "!", 1, MSG_OOB); + res = send (HANDLE2SOCKET (ctx->outbound.fd), fdpass_msg, msglen, 0); return 0; } static int -process_fdpass_msg (const char *fdpass_msg, size_t msglen, assuan_fd_t *r_fd) +process_fdpass_msg (const char *fdpass_msg, size_t msglen, int *r_fd) { - fprintf (stderr, "process_fdpass_msg: %s\n", fdpass_msg); - *r_fd = NULL; - /* not implemented yet, but pretending as if it's successful. */ + 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--; + return 0; } @@ -201,55 +316,69 @@ __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { fd_set fds; int tries = 3; - struct timeval timeout; + fd_set efds; + try_again: - timeout.tv_sec = 0; - timeout.tv_usec = 0; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); - res = select (0, NULL, NULL, &fds, &timeout); + 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 (res) + else if (FD_ISSET (HANDLE2SOCKET (fd), &efds)) { - assuan_fd_t fd_recv; + int fd_recv; char fdpass_msg[256]; + /* 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) - ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd_recv; - else + if (res < 0) { gpg_err_set_errno (EIO); return -1; } - } - again: - ec = 0; - res = recv (HANDLE2SOCKET (fd), buffer, size, 0); - if (res == -1) - ec = WSAGetLastError (); - if (ec == WSAEWOULDBLOCK && tries--) + ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = (assuan_fd_t)fd_recv; + goto try_again; + } + else { - /* EAGAIN: Use select to wait for resources and try again. - We do this 3 times and then give up. The higher level - 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_ZERO (&fds); - FD_SET (HANDLE2SOCKET (fd), &fds); - select (0, &fds, NULL, NULL, NULL); - goto again; + again: + ec = 0; + res = recv (HANDLE2SOCKET (fd), buffer, size, 0); + if (res == -1) + ec = WSAGetLastError (); + if (ec == WSAEWOULDBLOCK && tries--) + { + /* EAGAIN: Use select to wait for resources and try again. + We do this 3 times and then give up. The higher level + 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_ZERO (&fds); + FD_SET (HANDLE2SOCKET (fd), &fds); + select (0, &fds, NULL, NULL, NULL); + goto again; + } } } else |