/* system-w32.c - System support functions for Windows.
* Copyright (C) 2009, 2010 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 2.1 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "assuan-defs.h"
#include "debug.h"
assuan_fd_t
assuan_fdopen (int fd)
{
assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd);
assuan_fd_t ofd;
if (! DuplicateHandle(GetCurrentProcess(), ifd,
GetCurrentProcess(), &ofd, 0,
TRUE, DUPLICATE_SAME_ACCESS))
{
gpg_err_set_errno (EIO);
return ASSUAN_INVALID_FD;
}
return ofd;
}
/* Sleep for the given number of microseconds. Default
implementation. */
void
__assuan_usleep (assuan_context_t ctx, unsigned int usec)
{
if (!usec)
return;
Sleep (usec / 1000);
}
/* Close the given file descriptor, created with _assuan_pipe or one
of the socket functions. Default implementation. */
int
__assuan_close (assuan_context_t ctx, assuan_fd_t fd)
{
int rc = closesocket (HANDLE2SOCKET(fd));
if (rc)
gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) );
if (rc && WSAGetLastError () == WSAENOTSOCK)
{
rc = CloseHandle (fd);
if (rc)
/* FIXME. */
gpg_err_set_errno (EIO);
}
return rc;
}
/* Get a file HANDLE for other end to send, from MY_HANDLE. */
static gpg_error_t
get_file_handle (assuan_context_t ctx, assuan_fd_t my_handle,
int process_id, HANDLE *r_handle)
{
HANDLE prochandle, newhandle;
prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, process_id);
if (!prochandle)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_sendfd", ctx,
"OpenProcess failed: %s", _assuan_w32_strerror (ctx, -1));
return _assuan_error (ctx, gpg_err_code_from_errno (EIO));
}
if (!DuplicateHandle (GetCurrentProcess (), my_handle, prochandle, &newhandle,
0, TRUE, DUPLICATE_SAME_ACCESS))
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_sendfd", ctx,
"DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1));
CloseHandle (prochandle);
return _assuan_error (ctx, GPG_ERR_ASS_PARAMETER);
}
CloseHandle (prochandle);
*r_handle = newhandle;
return 0;
}
/* Send an FD (which means Windows HANDLE) to the peer. */
gpg_error_t
w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd)
{
char fdpass_msg[256];
int res;
HANDLE file_handle = INVALID_HANDLE_VALUE;
gpg_error_t err;
if (ctx->process_id == -1)
return _assuan_error (ctx, GPG_ERR_SERVER_FAILED);
err = get_file_handle (ctx, fd, ctx->process_id, &file_handle);
if (err)
return err;
res = snprintf (fdpass_msg, sizeof (fdpass_msg), "SENDFD %p", file_handle);
if (res < 0)
{
CloseHandle (file_handle);
return _assuan_error (ctx, GPG_ERR_ASS_PARAMETER);
}
err = assuan_transact (ctx, fdpass_msg, NULL, NULL, NULL, NULL, NULL, NULL);
return err;
}
/* Receive a HANDLE from the peer and turn it into an FD. */
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)
{
int res;
int ec = 0;
if (ctx->flags.is_socket)
{
int tries = 3;
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_set fds;
FD_ZERO (&fds);
FD_SET (HANDLE2SOCKET (fd), &fds);
select (0, &fds, NULL, NULL, NULL);
goto again;
}
}
else
{
DWORD nread = 0;
if (!ReadFile (fd, buffer, size, &nread, NULL))
{
res = -1;
ec = GetLastError ();
}
else
res = nread;
}
if (res == -1)
{
switch (ec)
{
case WSAENOTSOCK:
gpg_err_set_errno (EBADF);
break;
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case WSAECONNRESET: /* Due to the use of recv. */
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return res;
}
ssize_t
__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
size_t size)
{
int res;
int ec = 0;
if (ctx->flags.is_socket)
{
int tries = 3;
again:
ec = 0;
res = send (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_set fds;
FD_ZERO (&fds);
FD_SET (HANDLE2SOCKET (fd), &fds);
select (0, NULL, &fds, NULL, NULL);
goto again;
}
}
else
{
DWORD nwrite;
if (!WriteFile (fd, buffer, size, &nwrite, NULL))
{
res = -1;
ec = GetLastError ();
}
else
res = (int)nwrite;
}
if (res == -1)
{
switch (ec)
{
case WSAENOTSOCK:
gpg_err_set_errno (EBADF);
break;
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case ERROR_BROKEN_PIPE:
case ERROR_NO_DATA:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return res;
}
int
__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
gpg_err_set_errno (ENOSYS);
return -1;
}
int
__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
gpg_err_set_errno (ENOSYS);
return -1;
}
int
__assuan_socketpair (assuan_context_t ctx, int namespace, int style,
int protocol, assuan_fd_t filedes[2])
{
gpg_err_set_errno (ENOSYS);
return -1;
}
assuan_fd_t
__assuan_socket (assuan_context_t ctx, int domain, int type, int proto)
{
assuan_fd_t res;
res = SOCKET2HANDLE (socket (domain, type, proto));
if (res == SOCKET2HANDLE (INVALID_SOCKET))
gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
return res;
}
int
__assuan_connect (assuan_context_t ctx, assuan_fd_t sock,
struct sockaddr *addr, socklen_t length)
{
int res;
res = connect (HANDLE2SOCKET (sock), addr, length);
if (res < 0)
gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
return res;
}