Page MenuHome GnuPG

No OneTemporary

diff --git a/src/system-w32ce.c b/src/system-w32ce.c
index 1718bb7..d3e3357 100644
--- a/src/system-w32ce.c
+++ b/src/system-w32ce.c
@@ -1,705 +1,705 @@
/* system-w32ce.c - System support functions for WindowsCE.
Copyright (C) 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <time.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
-# endif
+# endif
#include <windows.h>
#include <winioctl.h>
#include <devload.h>
#include "assuan-defs.h"
#include "debug.h"
#define GPGCEDEV_IOCTL_GET_RVID \
CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define GPGCEDEV_IOCTL_MAKE_PIPE \
CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS)
static wchar_t *
utf8_to_wchar (const char *string)
{
int n;
size_t nbytes;
wchar_t *result;
if (!string)
return NULL;
n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0);
if (n < 0)
{
gpg_err_set_errno (EINVAL);
return NULL;
}
nbytes = (size_t)(n+1) * sizeof(*result);
- if (nbytes / sizeof(*result) != (n+1))
+ if (nbytes / sizeof(*result) != (n+1))
{
gpg_err_set_errno (ENOMEM);
return NULL;
}
result = malloc (nbytes);
if (!result)
return NULL;
n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n);
if (n < 0)
{
free (result);
gpg_err_set_errno (EINVAL);
result = NULL;
}
return result;
}
/* Convenience function. */
static void
free_wchar (wchar_t *string)
{
if (string)
free (string);
}
assuan_fd_t
assuan_fdopen (int fd)
{
return (assuan_fd_t)fd;
}
/* Sleep for the given number of microseconds. Default
implementation. */
void
__assuan_usleep (assuan_context_t ctx, unsigned int usec)
{
if (usec)
Sleep (usec / 1000);
}
/* Prepare a pipe. Returns a handle which is, depending on WRITE_END,
will either act the read or as the write end of the pipe. The
other value returned is a rendezvous id used to complete the pipe
creation with _assuan_w32ce_finish_pipe. The rendezvous id may be
passed to another process and that process may finish the pipe
creation. This creates the interprocess pipe. The rendezvous id
is not a handle but a plain number; there is no release function
and care should be taken not to pass it to a function expecting a
handle. */
HANDLE
_assuan_w32ce_prepare_pipe (int *r_rvid, int write_end)
{
HANDLE hd;
LONG rvid;
ActivateDevice (L"Drivers\\GnuPG_Device", 0);
/* Note: Using "\\$device\\GPG1" should be identical to "GPG1:".
However this returns an invalid parameter error without having
called GPG_Init in the driver. The docs mention something about
RegisterAFXEx but that API is not documented. */
hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hd != INVALID_HANDLE_VALUE)
{
if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_GET_RVID,
NULL, 0, &rvid, sizeof rvid, NULL, NULL))
{
DWORD lastrc = GetLastError ();
CloseHandle (hd);
hd = INVALID_HANDLE_VALUE;
SetLastError (lastrc);
}
else
*r_rvid = rvid;
}
-
+
return hd;
}
/* Create a pipe. WRITE_END shall have the opposite value of the one
pssed to _assuan_w32ce_prepare_pipe; see there for more
details. */
HANDLE
_assuan_w32ce_finish_pipe (int rvid, int write_end)
{
HANDLE hd;
if (!rvid)
{
SetLastError (ERROR_INVALID_HANDLE);
return INVALID_HANDLE_VALUE;
}
hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if (hd != INVALID_HANDLE_VALUE)
{
if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE,
&rvid, sizeof rvid, NULL, 0, NULL, NULL))
{
DWORD lastrc = GetLastError ();
CloseHandle (hd);
hd = INVALID_HANDLE_VALUE;
SetLastError (lastrc);
}
}
return hd;
}
/* WindowsCE does not provide a pipe feature. However we need
something like a pipe to convey data between processes and in some
cases within a process. This replacement is not only used by
libassuan but exported and thus usable by gnupg and gpgme as well. */
DWORD
_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd,
LPSECURITY_ATTRIBUTES sec_attr, DWORD size)
{
HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
int rvid;
int rc = 0;
hd[0] = _assuan_w32ce_prepare_pipe (&rvid, 0);
if (hd[0] != INVALID_HANDLE_VALUE)
{
hd[1] = _assuan_w32ce_finish_pipe (rvid, 1);
if (hd[1] != INVALID_HANDLE_VALUE)
rc = 1;
else
{
DWORD lastrc = GetLastError ();
CloseHandle (hd[0]);
hd[0] = INVALID_HANDLE_VALUE;
SetLastError (lastrc);
}
}
-
+
*read_hd = hd[0];
*write_hd = hd[1];
return rc;
}
/* Create a pipe with one inheritable end. Default implementation.
If INHERIT_IDX is 0, the read end of the pipe is made inheritable;
with INHERIT_IDX is 1 the write end will be inheritable. The
question now is how we create an inheritable pipe end under windows
CE were handles are process local objects? The trick we employ is
to defer the actual creation to the other end: We create an
incomplete pipe and pass a rendezvous id to the other end
(process). The other end now uses the rendezvous id to lookup the
pipe in our device driver, creates a new handle and uses that one
to finally establish the pipe. */
int
__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
{
HANDLE hd;
int rvid;
hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
if (hd == INVALID_HANDLE_VALUE)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx,
"CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1));
gpg_err_set_errno (EIO);
return -1;
}
if (inherit_idx)
{
fd[0] = hd;
fd[1] = (void*)rvid;
}
else
{
fd[0] = (void*)rvid;
fd[1] = hd;
}
return 0;
}
/* 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));
int err = WSAGetLastError ();
/* Note that gpg_err_set_errno on Windows CE overwrites
WSAGetLastError() (via SetLastError()). */
if (rc)
gpg_err_set_errno (_assuan_sock_wsa2errno (err));
if (rc && err == WSAENOTSOCK)
{
rc = CloseHandle (fd);
if (rc)
/* FIXME. */
gpg_err_set_errno (EIO);
}
return rc;
}
-static ssize_t
+ssize_t
__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
{
/* Due to the peculiarities of the W32 API we can't use read for a
network socket and thus we try to use recv first and fallback to
read if recv detects that it is not a network socket. */
int res;
TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx,
"fd=0x%x, buffer=%p, size=%i", fd, buffer, size);
#ifdef HAVE_W32CE_SYSTEM
/* This is a bit of a hack to support stdin over ssh. Note that
fread buffers fully while getchar is line buffered. Weird, but
that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are
special handle values that shouldn't occur in the wild. */
if (fd == ASSUAN_STDIN)
{
int i = 0;
int chr;
while (i < size)
{
chr = getchar();
if (chr == EOF)
break;
((char*)buffer)[i++] = (char) chr;
if (chr == '\n')
break;
}
return TRACE_SYSRES (i);
}
#endif
res = recv (HANDLE2SOCKET (fd), buffer, size, 0);
if (res == -1)
{
TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ());
switch (WSAGetLastError ())
{
case WSAENOTSOCK:
{
DWORD nread = 0;
-
+
res = ReadFile (fd, buffer, size, &nread, NULL);
if (! res)
{
TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ());
switch (GetLastError ())
{
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
case ERROR_PIPE_NOT_CONNECTED:
case ERROR_BUSY:
gpg_err_set_errno (EAGAIN);
break;
default:
- gpg_err_set_errno (EIO);
+ gpg_err_set_errno (EIO);
}
res = -1;
}
else
res = (int) nread;
}
break;
-
+
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return TRACE_SYSRES (res);
}
-static ssize_t
+ssize_t
__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
size_t size)
{
/* Due to the peculiarities of the W32 API we can't use write for a
network socket and thus we try to use send first and fallback to
write if send detects that it is not a network socket. */
int res;
TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx,
"fd=0x%x, buffer=%p, size=%i", fd, buffer, size);
#ifdef HAVE_W32CE_SYSTEM
/* This is a bit of a hack to support stdout over ssh. Note that
fread buffers fully while getchar is line buffered. Weird, but
that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are
special handle values that shouldn't occur in the wild. */
if (fd == ASSUAN_STDOUT)
{
res = fwrite (buffer, 1, size, stdout);
return TRACE_SYSRES (res);
}
#endif
res = send ((int)fd, buffer, size, 0);
if (res == -1 && WSAGetLastError () == WSAENOTSOCK)
{
DWORD nwrite;
TRACE_LOG ("send call failed - trying WriteFile");
res = WriteFile (fd, buffer, size, &nwrite, NULL);
if (! res)
{
TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ());
switch (GetLastError ())
{
- case ERROR_BROKEN_PIPE:
+ case ERROR_BROKEN_PIPE:
case ERROR_NO_DATA:
gpg_err_set_errno (EPIPE);
break;
case ERROR_PIPE_NOT_CONNECTED:
case ERROR_BUSY:
gpg_err_set_errno (EAGAIN);
break;
-
+
default:
gpg_err_set_errno (EIO);
break;
}
res = -1;
}
else
res = (int) nwrite;
}
else if (res == -1)
TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ());
return TRACE_SYSRES (res);
}
-static int
+int
__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
gpg_err_set_errno (ENOSYS);
return -1;
}
-static int
+int
__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
gpg_err_set_errno (ENOSYS);
return -1;
}
/* Build a command line for use with W32's CreateProcess. On success
CMDLINE gets the address of a newly allocated string. */
static int
build_w32_commandline (assuan_context_t ctx, const char * const *argv,
assuan_fd_t fd0, assuan_fd_t fd1, assuan_fd_t fd2,
int fd2_isnull,
char **cmdline)
{
int i, n;
const char *s;
char *buf, *p;
char fdbuf[3*30];
p = fdbuf;
*p = 0;
if (fd0 != ASSUAN_INVALID_FD)
{
snprintf (p, 25, "-&S0=%d ", (int)fd0);
p += strlen (p);
}
if (fd1 != ASSUAN_INVALID_FD)
{
snprintf (p, 25, "-&S1=%d ", (int)fd1);
p += strlen (p);
}
if (fd2 != ASSUAN_INVALID_FD)
{
if (fd2_isnull)
strcpy (p, "-&S2=null ");
else
snprintf (p, 25, "-&S2=%d ", (int)fd2);
p += strlen (p);
}
-
+
*cmdline = NULL;
n = strlen (fdbuf);
for (i=0; (s = argv[i]); i++)
{
if (!i)
continue; /* Ignore argv[0]. */
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
for (; *s; s++)
if (*s == '\"')
n++; /* Need to double inner quotes. */
}
n++;
buf = p = _assuan_malloc (ctx, n);
if (! buf)
return -1;
p = stpcpy (p, fdbuf);
- for (i = 0; argv[i]; i++)
+ for (i = 0; argv[i]; i++)
{
if (!i)
continue; /* Ignore argv[0]. */
if (i > 1)
p = stpcpy (p, " ");
if (! *argv[i]) /* Empty string. */
p = stpcpy (p, "\"\"");
else if (strpbrk (argv[i], " \t\n\v\f\""))
{
p = stpcpy (p, "\"");
for (s = argv[i]; *s; s++)
{
*p++ = *s;
if (*s == '\"')
*p++ = *s;
}
*p++ = '\"';
*p = 0;
}
else
p = stpcpy (p, argv[i]);
}
*cmdline = buf;
return 0;
}
int
__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
const char **argv,
assuan_fd_t fd_in, assuan_fd_t fd_out,
assuan_fd_t *fd_child_list,
void (*atfork) (void *opaque, int reserved),
void *atforkvalue, unsigned int flags)
{
- PROCESS_INFORMATION pi =
+ PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
assuan_fd_t fd;
assuan_fd_t *fdp;
assuan_fd_t fd_err;
int fd_err_isnull = 0;
char *cmdline;
/* Dup stderr to /dev/null unless it is in the list of FDs to be
passed to the child. Well we don't actually open nul because
that is not available on Windows, but use our hack for it.
Because an RVID of 0 is an invalid value and HANDLES will never
have this value either, we test for this as well. */
/* FIXME: As long as we can't decide whether a handle is a real
handler or an rendezvous id we can't do anything with the
FD_CHILD_LIST. We can't do much with stderr either, thus we
better don't pass stderr to the child at all. If we would do so
and it is not a rendezvous id the client would run into
problems. */
fd = assuan_fd_from_posix_fd (fileno (stderr));
fdp = fd_child_list;
if (fdp)
{
for (; *fdp != ASSUAN_INVALID_FD && *fdp != 0 && *fdp != fd; fdp++)
;
}
if (!fdp || *fdp == ASSUAN_INVALID_FD)
fd_err_isnull = 1;
fd_err = ASSUAN_INVALID_FD;
if (build_w32_commandline (ctx, argv, fd_in, fd_out, fd_err, fd_err_isnull,
&cmdline))
{
return -1;
}
TRACE2 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"path=`%s' cmdline=`%s'", name, cmdline);
{
wchar_t *wcmdline, *wname;
wcmdline = utf8_to_wchar (cmdline);
_assuan_free (ctx, cmdline);
if (!wcmdline)
return -1;
wname = utf8_to_wchar (name);
if (!wname)
{
free_wchar (wcmdline);
return -1;
}
-
+
if (!CreateProcess (wname, /* Program to start. */
wcmdline, /* Command line arguments. */
NULL, /* (not supported) */
NULL, /* (not supported) */
FALSE, /* (not supported) */
(CREATE_SUSPENDED), /* Creation flags. */
NULL, /* (not supported) */
NULL, /* (not supported) */
NULL, /* (not supported) */
&pi /* Returns process information.*/
))
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1));
free_wchar (wname);
free_wchar (wcmdline);
gpg_err_set_errno (EIO);
return -1;
}
free_wchar (wname);
free_wchar (wcmdline);
}
ResumeThread (pi.hThread);
TRACE4 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"CreateProcess ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId);
- CloseHandle (pi.hThread);
-
+ CloseHandle (pi.hThread);
+
*r_pid = (pid_t) pi.hProcess;
return 0;
}
-static pid_t
+pid_t
__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait,
int *status, int options)
{
CloseHandle ((HANDLE) pid);
return 0;
}
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;
}
int
__assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol)
{
int res;
- res = socket (domain, type, proto);
+ res = socket (namespace, style, protocol);
if (res == -1)
gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
return res;
}
int
__assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
socklen_t length)
{
int res;
res = connect (sock, addr, length);
if (res < 0)
gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
return res;
}
/* The default system hooks for assuan contexts. */
struct assuan_system_hooks _assuan_system_hooks =
{
ASSUAN_SYSTEM_HOOKS_VERSION,
__assuan_usleep,
__assuan_pipe,
__assuan_close,
__assuan_read,
__assuan_write,
__assuan_recvmsg,
__assuan_sendmsg,
__assuan_spawn,
__assuan_waitpid,
__assuan_socketpair,
__assuan_socket,
- __assuan_connect
+ __assuan_connect
};
diff --git a/tests/ce-server.c b/tests/ce-server.c
index 24cf734..0565aee 100644
--- a/tests/ce-server.c
+++ b/tests/ce-server.c
@@ -1,1418 +1,1418 @@
/* ce-server.c - An Assuan testbed for W32CE; server code
Copyright (C) 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 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 <unistd.h>
#include <sys/stat.h>
#ifdef HAVE_W32_SYSTEM
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
-# else
+# endif
#endif
#include <errno.h>
#ifdef HAVE_W32CE_SYSTEM
# ifndef FILE_ATTRIBUTE_ROMSTATICREF
# define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE
# endif
extern BOOL GetStdioPathW (int, wchar_t *, DWORD *);
extern BOOL SetStdioPathW (int, const wchar_t *);
#endif /*!HAVE_W32CE_SYSTEM*/
#include "../src/assuan.h"
#include "common.h"
/* The port we are using by default. */
static short server_port = 15898;
/* Flag set to indicate a shutdown. */
static int shutdown_pending;
/* An object to keep track of file descriptors. */
struct fdinfo_s
{
struct fdinfo_s *next;
assuan_fd_t fd; /* The descriptor. */
};
typedef struct fdinfo_s *fdinfo_t;
/* The local state of a connection. */
struct state_s
{
/* The current working directory - access using get_cwd(). */
char *cwd;
/* If valid, a socket in listening state created by the dataport
command. */
assuan_fd_t dataport_listen_fd;
/* If valid the socket accepted for the dataport. */
assuan_fd_t dataport_accepted_fd;
/* The error code from a dataport accept operation. */
gpg_error_t dataport_accept_err;
/* A list of all unused descriptors created by dataport commands. */
fdinfo_t dataport_fds;
/* The descriptor set by the DATAPORT command. */
assuan_fd_t dataport_fd;
};
typedef struct state_s *state_t;
/* Local prototypes. */
static gpg_error_t cmd_newdataport_cont (void *opaque, gpg_error_t err,
unsigned char *data, size_t datalen);
/* A wrapper around read to make it work under Windows with HANDLES
and socket descriptors. Takes care of EINTR on POSIX. */
static int
my_read (assuan_fd_t fd, void *buffer, size_t size)
{
int res;
#ifdef HAVE_W32_SYSTEM
res = recv (HANDLE2SOCKET (fd), buffer, size, 0);
if (res == -1)
{
switch (WSAGetLastError ())
{
case WSAENOTSOCK:
{
DWORD nread = 0;
res = ReadFile (fd, buffer, size, &nread, NULL);
if (!res)
{
switch (GetLastError ())
{
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
}
res = -1;
}
else
res = (int) nread;
}
break;
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return res;
#else /*!HAVE_W32_SYSTEM*/
do
res = read (fd, buffer, size);
while (res == -1 && errno == EINTR);
return res;
#endif /*!HAVE_W32_SYSTEM*/
}
/* Extended version of write(2) to guarantee that all bytes are
written. Returns 0 on success or -1 and ERRNO on failure. NOTE:
This function does not return the number of bytes written, so any
error must be treated as fatal for this connection as the state of
the receiver is unknown. This works best if blocking is allowed
(so EAGAIN cannot occur). Under Windows this function handles
socket descriptors and system handles. */
static int
my_writen (assuan_fd_t fd, const char *buffer, size_t length)
{
while (length)
{
int nwritten;
#ifdef HAVE_W32_SYSTEM
nwritten = send (HANDLE2SOCKET (fd), buffer, length, 0);
if (nwritten == -1 && WSAGetLastError () == WSAENOTSOCK)
{
DWORD nwrite;
nwritten = WriteFile (fd, buffer, length, &nwrite, NULL);
if (!nwritten)
{
switch (GetLastError ())
{
case ERROR_BROKEN_PIPE:
case ERROR_NO_DATA:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
nwritten= -1;
}
else
nwritten = (int)nwrite;
}
#else /*!HAVE_W32_SYSTEM*/
nwritten = write (fd, buffer, length);
#endif /*!HAVE_W32_SYSTEM*/
if (nwritten < 0)
{
if (errno == EINTR)
continue;
return -1; /* write error */
}
length -= nwritten;
buffer += nwritten;
}
return 0; /* okay */
}
static state_t
new_state (void)
{
state_t state = xcalloc (1, sizeof *state);
state->dataport_listen_fd = ASSUAN_INVALID_FD;
state->dataport_accepted_fd = ASSUAN_INVALID_FD;
state->dataport_fd = ASSUAN_INVALID_FD;
return state;
}
static void
release_state (state_t state)
{
fdinfo_t fi, fi2;
if (!state)
return;
xfree (state->cwd);
if (state->dataport_fd != ASSUAN_INVALID_FD)
assuan_sock_close (state->dataport_fd);
if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
assuan_sock_close (state->dataport_listen_fd);
if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
assuan_sock_close (state->dataport_accepted_fd);
for (fi=state->dataport_fds; fi; fi = fi2)
{
fi2 = fi->next;
if (fi->fd != ASSUAN_INVALID_FD)
assuan_sock_close (fi->fd);
}
xfree (state);
}
/* Helper to print a message while leaving a command and to
acknowledge the command. */
static gpg_error_t
leave_cmd (assuan_context_t ctx, gpg_error_t err)
{
if (err)
{
const char *name = assuan_get_command_name (ctx);
if (!name)
name = "?";
if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
else
log_error ("command '%s' failed: %s <%s>\n", name,
gpg_strerror (err), gpg_strsource (err));
}
return assuan_process_done (ctx, err);
}
#ifdef HAVE_W32CE_SYSTEM
static char *
wchar_to_utf8 (const wchar_t *string)
{
int n;
size_t length = wcslen (string);
char *result;
n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL);
if (n < 0 || (n+1) <= 0)
log_fatal ("WideCharToMultiByte failed\n");
result = xmalloc (n+1);
n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL);
if (n < 0)
log_fatal ("WideCharToMultiByte failed\n");
result[n] = 0;
return result;
}
static wchar_t *
utf8_to_wchar (const char *string)
{
int n;
size_t length = strlen (string);
wchar_t *result;
size_t nbytes;
n = MultiByteToWideChar (CP_UTF8, 0, string, length, NULL, 0);
if (n < 0 || (n+1) <= 0)
log_fatal ("MultiByteToWideChar failed\n");
nbytes = (size_t)(n+1) * sizeof(*result);
if (nbytes / sizeof(*result) != (n+1))
log_fatal ("utf8_to_wchar: integer overflow\n");
result = xmalloc (nbytes);
n = MultiByteToWideChar (CP_UTF8, 0, string, length, result, n);
if (n < 0)
log_fatal ("MultiByteToWideChar failed\n");
result[n] = 0;
return result;
}
#endif /*HAVE_W32CE_SYSTEM*/
#ifndef HAVE_W32CE_SYSTEM
static char *
gnu_getcwd (void)
{
size_t size = 100;
while (1)
{
char *buffer = xmalloc (size);
if (getcwd (buffer, size) == buffer)
return buffer;
xfree (buffer);
if (errno != ERANGE)
return 0;
size *= 2;
}
}
#endif /*!HAVE_W32CE_SYSTEM*/
/* Return the current working directory. The returned string is valid
as long as STATE->cwd is not changed. */
static const char *
get_cwd (state_t state)
{
if (!state->cwd)
{
/* No working directory yet. On WindowsCE make it the module
directory of this process. */
#ifdef HAVE_W32_SYSTEM
char *p;
#endif
#ifdef HAVE_W32CE_SYSTEM
wchar_t buf[MAX_PATH+1];
size_t n;
n = GetModuleFileName (NULL, buf, MAX_PATH);
if (!n)
state->cwd = xstrdup ("/");
else
{
buf[n] = 0;
state->cwd = wchar_to_utf8 (buf);
p = strrchr (state->cwd, '\\');
if (p)
*p = 0;
}
#else
state->cwd = gnu_getcwd ();
#endif
#ifdef HAVE_W32_SYSTEM
for (p=state->cwd; *p; p++)
if (*p == '\\')
*p = '/';
#endif /*HAVE_W32_SYSTEM*/
}
return state->cwd;
}
static gpg_error_t
reset_notify (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
fdinfo_t fi, fi2;
/* Close all lingering dataport connections. */
for (fi=state->dataport_fds; fi; fi = fi2)
{
fi2 = fi->next;
if (fi->fd != ASSUAN_INVALID_FD)
assuan_sock_close (fi->fd);
}
state->dataport_fds = NULL;
return 0;
}
static gpg_error_t
input_notify (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
assuan_fd_t fd = assuan_get_input_fd (ctx);
fdinfo_t fi;
if (fd != ASSUAN_INVALID_FD)
{
/* The fd is now in use use - remove it from the unused list. */
for (fi=state->dataport_fds; fi; fi = fi->next)
if (fi->fd == fd)
fi->fd = ASSUAN_INVALID_FD;
}
return 0;
}
static gpg_error_t
output_notify (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
assuan_fd_t fd = assuan_get_output_fd (ctx);
fdinfo_t fi;
if (fd != ASSUAN_INVALID_FD)
{
/* The fd is now in use - remove it from the unused list. */
for (fi=state->dataport_fds; fi; fi = fi->next)
if (fi->fd == fd)
fi->fd = ASSUAN_INVALID_FD;
}
return 0;
}
static const char hlp_echo[] =
"ECHO <line>\n"
"\n"
"Print LINE as data lines.\n";
static gpg_error_t
cmd_echo (assuan_context_t ctx, char *line)
{
gpg_error_t err;
err = assuan_send_data (ctx, line, strlen (line));
return leave_cmd (ctx, err);
}
static const char hlp_cat[] =
"CAT [<filename>]\n"
"\n"
"Copy the content of FILENAME to the descriptor set by the OUTPUT\n"
"command. If no OUTPUT command has been given, send the content\n"
"using data lines. Without FILENAME take the content from the\n"
"descriptor set by the INPUT command; if a DATAPORT has been set\n"
"this descriptor is used for I/O and the INOPUT/OUTPUT descriptors\n"
"are not touched.";
static gpg_error_t
cmd_cat (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err = 0;
assuan_fd_t fd_in = ASSUAN_INVALID_FD;
assuan_fd_t fd_out = ASSUAN_INVALID_FD;
FILE *fp_in = NULL;
char buf[256];
size_t nread;
int use_dataport = 0;
if (*line)
{
fp_in = fopen (line, "rb");
if (!fp_in)
err = gpg_error_from_syserror ();
else
fd_out = assuan_get_output_fd (ctx);
}
else if (state->dataport_fd != ASSUAN_INVALID_FD)
{
use_dataport = 1;
fd_in = state->dataport_fd;
fd_out = state->dataport_fd;
}
else if ((fd_in = assuan_get_input_fd (ctx)) != ASSUAN_INVALID_FD)
{
/* This FD is actually a socket descriptor. We can't fdopen it
because under Windows we ust use recv(2) instead of read(2).
Note that on POSIX systems there is no difference between
libc file descriptors and socket descriptors. */
fd_out = assuan_get_output_fd (ctx);
}
else
err = gpg_error (GPG_ERR_ASS_NO_INPUT);
if (err)
goto leave;
do
{
if (fp_in)
{
nread = fread (buf, 1, sizeof buf, fp_in);
if (nread < sizeof buf)
{
if (ferror (fp_in))
err = gpg_error_from_syserror ();
else if (feof (fp_in))
err = gpg_error (GPG_ERR_EOF);
}
}
else
{
int n;
nread = 0;
n = my_read (fd_in, buf, sizeof buf);
if (n < 0)
err = gpg_error_from_syserror ();
else if (!n)
err = gpg_error (GPG_ERR_EOF);
else
nread = n;
}
if (fd_out != ASSUAN_INVALID_FD)
{
if (nread && my_writen (fd_out, buf, nread))
err = gpg_error_from_syserror ();
}
else if (nread)
err = assuan_send_data (ctx, buf, nread);
}
while (!err);
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
leave:
if (fp_in)
fclose (fp_in);
if (use_dataport)
{
if (state->dataport_fd != ASSUAN_INVALID_FD)
{
assuan_sock_close (state->dataport_fd);
state->dataport_fd = ASSUAN_INVALID_FD;
}
}
else
{
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
}
return leave_cmd (ctx, err);
}
static const char hlp_pwd[] =
"PWD\n"
"\n"
"Print the curent working directory of this session.\n";
static gpg_error_t
cmd_pwd (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err;
const char *string;
string = get_cwd (state);
err = assuan_send_data (ctx, string, strlen (string));
return leave_cmd (ctx, err);
}
static const char hlp_cd[] =
"CD [dir]\n"
"\n"
"Change the curretn directory of the session.\n";
static gpg_error_t
cmd_cd (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err = 0;
char *newdir, *p;
for (p=line; *p; p++)
if (*p == '\\')
*p = '/';
if (!*line)
{
xfree (state->cwd);
state->cwd = NULL;
get_cwd (state);
}
else
{
if (*line == '/')
newdir = xstrdup (line);
else
newdir = xstrconcat (get_cwd (state), "/", line, NULL);
while (strlen(newdir) > 1 && line[strlen(newdir)-1] == '/')
line[strlen(newdir)-1] = 0;
xfree (state->cwd);
state->cwd = newdir;
}
return leave_cmd (ctx, err);
}
#ifdef HAVE_W32CE_SYSTEM
static const char hlp_ls[] =
"LS [<pattern>]\n"
"\n"
"List the files described by PATTERN.\n";
static gpg_error_t
cmd_ls (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err;
WIN32_FIND_DATA fi;
char buf[500];
HANDLE hd;
char *p, *fname;
wchar_t *wfname;
if (!*line)
fname = xstrconcat (get_cwd (state), "/*", NULL);
else if (*line == '/' || *line == '\\')
fname = xstrdup (line);
else
fname = xstrconcat (get_cwd (state), "/", line, NULL);
for (p=fname; *p; p++)
if (*p == '/')
*p = '\\';
assuan_write_status (ctx, "PATTERN", fname);
wfname = utf8_to_wchar (fname);
xfree (fname);
hd = FindFirstFile (wfname, &fi);
free (wfname);
if (hd == INVALID_HANDLE_VALUE)
{
log_info ("FindFirstFile returned %d\n", GetLastError ());
err = gpg_error_from_syserror (); /* Works for W32CE. */
goto leave;
}
do
{
DWORD attr = fi.dwFileAttributes;
fname = wchar_to_utf8 (fi.cFileName);
snprintf (buf, sizeof buf,
"%c%c%c%c%c%c%c%c%c%c%c%c%c %7lu%c %s\n",
(attr & FILE_ATTRIBUTE_DIRECTORY)
? ((attr & FILE_ATTRIBUTE_DEVICE)? 'c':'d'):'-',
(attr & FILE_ATTRIBUTE_READONLY)? 'r':'-',
(attr & FILE_ATTRIBUTE_HIDDEN)? 'h':'-',
(attr & FILE_ATTRIBUTE_SYSTEM)? 's':'-',
(attr & FILE_ATTRIBUTE_ARCHIVE)? 'a':'-',
(attr & FILE_ATTRIBUTE_COMPRESSED)? 'c':'-',
(attr & FILE_ATTRIBUTE_ENCRYPTED)? 'e':'-',
(attr & FILE_ATTRIBUTE_INROM)? 'R':'-',
(attr & FILE_ATTRIBUTE_REPARSE_POINT)? 'P':'-',
(attr & FILE_ATTRIBUTE_ROMMODULE)? 'M':'-',
(attr & FILE_ATTRIBUTE_ROMSTATICREF)? 'R':'-',
(attr & FILE_ATTRIBUTE_SPARSE_FILE)? 'S':'-',
(attr & FILE_ATTRIBUTE_TEMPORARY)? 't':'-',
(unsigned long)fi.nFileSizeLow,
fi.nFileSizeHigh? 'X':' ',
fname);
free (fname);
err = assuan_send_data (ctx, buf, strlen (buf));
if (!err)
err = assuan_send_data (ctx, NULL, 0);
}
while (!err && FindNextFile (hd, &fi));
if (err)
;
else if (GetLastError () == ERROR_NO_MORE_FILES)
err = 0;
else
{
log_info ("FindNextFile returned %d\n", GetLastError ());
err = gpg_error_from_syserror ();
}
FindClose (hd);
leave:
return leave_cmd (ctx, err);
}
#endif /*HAVE_W32CE_SYSTEM*/
#ifdef HAVE_W32CE_SYSTEM
static const char hlp_run[] =
"RUN <filename> [<args>]\n"
"\n"
"Run the program in FILENAME with the arguments ARGS.\n"
"This creates a new process and waits for it to finish.\n"
"FIXME: The process' stdin is connected to the file set by the\n"
"INPUT command; stdout and stderr to the one set by OUTPUT.\n";
static gpg_error_t
cmd_run (assuan_context_t ctx, char *line)
{
/* state_t state = assuan_get_pointer (ctx); */
gpg_error_t err;
BOOL w32ret;
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
char *p;
wchar_t *pgmname = NULL;
wchar_t *cmdline = NULL;
int code;
DWORD exc;
int idx;
struct {
HANDLE hd[2];
int oldname_valid;
wchar_t oldname[MAX_PATH];
} pipes[3];
for (idx=0; idx < 3; idx++)
{
pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE;
pipes[idx].oldname_valid = 0;
}
p = strchr (line, ' ');
if (p)
{
*p = 0;
pgmname = utf8_to_wchar (line);
for (p++; *p && *p == ' '; p++)
;
cmdline = utf8_to_wchar (p);
}
else
pgmname = utf8_to_wchar (line);
{
char *tmp1 = wchar_to_utf8 (pgmname);
char *tmp2 = wchar_to_utf8 (cmdline);
log_info ("CreateProcess, path=`%s' cmdline=`%s'\n", tmp1, tmp2);
xfree (tmp2);
xfree (tmp1);
}
/* Redirect the standard handles. */
/* Create pipes. */
for (idx=0; idx < 3; idx++)
{
if (!_assuan_w32ce_create_pipe (&pipes[idx].hd[0], &pipes[idx].hd[1],
NULL, 0))
{
err = gpg_error_from_syserror ();
log_error ("CreatePipe failed: %d\n", GetLastError ());
pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE;
goto leave;
}
}
/* Save currently assigned devices. */
for (idx=0; idx < 3; idx++)
{
DWORD dwlen = MAX_PATH;
if (!GetStdioPathW (idx, pipes[idx].oldname, &dwlen))
{
err = gpg_error_from_syserror ();
log_error ("GetStdioPath failed: %d\n", GetLastError ());
goto leave;
}
pipes[idx].oldname_valid = 1;
}
/* Connect the pipes. */
{
if (!SetStdioPathW (1, L"\\mystdout.log"))
{
err = gpg_error_from_syserror ();
log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ());
goto leave;
}
if (!SetStdioPathW (2, L"\\mystderr.log"))
{
err = gpg_error_from_syserror ();
log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ());
goto leave;
}
}
/* Create the process, restore the devices and check the error. */
w32ret = CreateProcess (pgmname, /* Program to start. */
cmdline, /* Command line arguments. */
NULL, /* Process security. Not used. */
NULL, /* Thread security. Not used. */
FALSE, /* Inherit handles. Not used. */
CREATE_SUSPENDED, /* Creation flags. */
NULL, /* Environment. Not used. */
NULL, /* Use current dir. Not used. */
NULL, /* Startup information. Not used. */
&pi /* Returns process information. */
);
for (idx=0; idx < 3; idx++)
{
if (pipes[idx].oldname_valid)
{
if (!SetStdioPathW (idx, pipes[idx].oldname))
log_error ("SetStdioPath(%d) failed during restore: %d\n",
idx, GetLastError ());
else
pipes[idx].oldname_valid = 0;
}
}
if (!w32ret)
{
/* Error checking after restore so that the messages are visible. */
log_error ("CreateProcess failed: %d\n", GetLastError ());
err = gpg_error_from_syserror ();
goto leave;
}
log_info ("CreateProcess ready: hProcess=%p hThread=%p"
" dwProcessID=%d dwThreadId=%d\n",
pi.hProcess, pi.hThread,
(int) pi.dwProcessId, (int) pi.dwThreadId);
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
code = WaitForSingleObject (pi.hProcess, INFINITE);
switch (code)
{
case WAIT_FAILED:
err = gpg_error_from_syserror ();;
log_error ("waiting for process %d to terminate failed: %d\n",
(int)pi.dwProcessId, GetLastError ());
break;
case WAIT_OBJECT_0:
if (!GetExitCodeProcess (pi.hProcess, &exc))
{
err = gpg_error_from_syserror ();;
log_error ("error getting exit code of process %d: %s\n",
(int)pi.dwProcessId, GetLastError () );
}
else if (exc)
{
log_info ("error running process: exit status %d\n", (int)exc);
err = gpg_error (GPG_ERR_GENERAL);
}
else
{
err = 0;
}
break;
default:
err = gpg_error_from_syserror ();;
log_error ("WaitForSingleObject returned unexpected "
"code %d for pid %d\n", code, (int)pi.dwProcessId);
break;
}
CloseHandle (pi.hProcess);
leave:
for (idx=0; idx < 3; idx++)
{
if (pipes[idx].oldname_valid)
{
if (!SetStdioPathW (idx, pipes[idx].oldname))
log_error ("SetStdioPath(%d) failed during restore: %d\n",
idx, GetLastError ());
else
pipes[idx].oldname_valid = 0;
}
}
for (idx=0; idx < 3; idx++)
{
if (pipes[idx].hd[0] != INVALID_HANDLE_VALUE)
CloseHandle (pipes[idx].hd[0]);
if (pipes[idx].hd[1] != INVALID_HANDLE_VALUE)
CloseHandle (pipes[idx].hd[1]);
}
xfree (cmdline);
xfree (pgmname);
return leave_cmd (ctx, err);
}
#endif /*HAVE_W32CE_SYSTEM*/
static const char hlp_newdataport[] =
"NEWDATAPORT\n"
"\n"
"Create a new dataport. The server creates a listening socket and\n"
"issues the inquiry:\n"
" INQUIRE CONNECT-TO <port>\n"
"The client is expected to connect to PORT of the server and confirm\n"
"this by sending just an \"END\". In turn the server sends:\n"
" S FDINFO <n>\n"
"With N being the local descriptor for the accepted connection. This\n"
"descriptor may now be used with INPUT or OUTPUT commands.";
struct cmd_dataport_locals
{
assuan_context_t ctx;
int passthru;
int port;
};
static gpg_error_t
cmd_newdataport (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err = 0;
struct sockaddr_in addr;
socklen_t addrlen;
struct cmd_dataport_locals *cont;
char inqline[100];
cont = xmalloc (sizeof *cont);
cont->ctx = ctx;
cont->passthru = 0;
cont->port = 0;
if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
{
log_error ("Oops, still listening on a dataport socket\n");
state->dataport_listen_fd = ASSUAN_INVALID_FD;
}
if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
{
log_error ("Oops, still holding an accepted dataport socket\n");
state->dataport_accepted_fd = ASSUAN_INVALID_FD;
}
state->dataport_accept_err = 0;
state->dataport_listen_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0);
if (state->dataport_listen_fd == ASSUAN_INVALID_FD)
{
err = gpg_error_from_syserror ();
log_error ("socket() failed: %s\n", strerror (errno));
goto leave;
}
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = htonl (INADDR_ANY);
if (assuan_sock_bind (state->dataport_listen_fd,
(struct sockaddr *)&addr, sizeof addr))
{
err = gpg_error_from_syserror ();
log_error ("listen() failed: %s\n", strerror (errno));
goto leave;
}
if (listen (HANDLE2SOCKET (state->dataport_listen_fd), 1))
{
err = gpg_error_from_syserror ();
log_error ("listen() failed: %s\n", strerror (errno));
goto leave;
}
addrlen = sizeof addr;
if (getsockname (HANDLE2SOCKET (state->dataport_listen_fd),
(struct sockaddr *)&addr, &addrlen))
{
err = gpg_error_from_syserror ();
log_error ("getsockname() failed: %s\n", strerror (errno));
goto leave;
}
cont->port = ntohs (addr.sin_port);
if (verbose)
log_info ("server now also listening on port %d\n", cont->port);
snprintf (inqline, sizeof inqline, "CONNECT-TO %d", cont->port);
err = assuan_inquire_ext (ctx, inqline, 0, cmd_newdataport_cont, cont);
if (!err)
return 0; /* Transfer to continuation. */
leave:
cont->passthru = 1;
return cmd_newdataport_cont (cont, err, NULL, 0);
}
/* Continuation used by cmd_newdataport. */
static gpg_error_t
cmd_newdataport_cont (void *opaque, gpg_error_t err,
unsigned char *data, size_t datalen)
{
struct cmd_dataport_locals *cont = opaque;
assuan_context_t ctx = cont->ctx;
state_t state = assuan_get_pointer (ctx);
char numbuf[35];
fdinfo_t fi;
if (cont->passthru || err)
goto leave;
err = state->dataport_accept_err;
if (err)
goto leave;
if (state->dataport_listen_fd != ASSUAN_INVALID_FD
|| state->dataport_accepted_fd == ASSUAN_INVALID_FD)
{
err = gpg_error (GPG_ERR_MISSING_ACTION);
goto leave;
}
for (fi = state->dataport_fds; fi; fi = fi->next)
if (fi->fd == ASSUAN_INVALID_FD)
break;
if (!fi)
{
fi = xcalloc (1, sizeof *fi);
fi->next = state->dataport_fds;
state->dataport_fds = fi;
}
fi->fd = state->dataport_accepted_fd;
state->dataport_accepted_fd = ASSUAN_INVALID_FD;
/* Note that under Windows the FD is the socket descriptor. Socket
descriptors are neither handles nor libc file descriptors. */
snprintf (numbuf, sizeof numbuf, "%d", HANDLE2SOCKET (fi->fd));
err = assuan_write_status (ctx, "FDINFO", numbuf);
leave:
if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
{
assuan_sock_close (state->dataport_listen_fd);
state->dataport_listen_fd = ASSUAN_INVALID_FD;
}
if (state->dataport_accepted_fd != ASSUAN_INVALID_FD)
{
assuan_sock_close (state->dataport_accepted_fd);
state->dataport_accepted_fd = ASSUAN_INVALID_FD;
}
xfree (cont);
return leave_cmd (ctx, err);
}
static const char hlp_dataport[] =
"DATAPORT FD[=<n>]\n"
"\n"
"Set the file descriptor to read and write data via port.\n"
"This is similar to the \"INPUT\" and \"OUTPUT\" commands\n"
"but useful for socketpairs.";
static gpg_error_t
cmd_dataport (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t fd;
if (state->dataport_fd != ASSUAN_INVALID_FD)
{
assuan_sock_close (state->dataport_fd);
state->dataport_fd = ASSUAN_INVALID_FD;
}
err = assuan_command_parse_fd (ctx, line, &fd);
if (!err && fd != ASSUAN_INVALID_FD)
{
fdinfo_t fi;
state->dataport_fd = fd;
/* The fd is now in use use - remove it from the unused list. */
for (fi=state->dataport_fds; fi; fi = fi->next)
if (fi->fd == fd)
fi->fd = ASSUAN_INVALID_FD;
}
return leave_cmd (ctx, err);
}
static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
"Multipurpose function to return a variety of information.\n"
"Supported values for WHAT are:\n"
"\n"
" version - Return the version of the program.\n"
" pid - Return the process id of the server.\n"
" dataports - Return a list of usused dataports.";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
state_t state = assuan_get_pointer (ctx);
gpg_error_t err = 0;
char numbuf[50];
if (!strcmp (line, "version"))
{
const char *s = VERSION;
err = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else if (!strcmp (line, "dataports"))
{
fdinfo_t fi;
int any = 0;
for (fi=state->dataport_fds; !err && fi; fi = fi->next)
{
if (fi->fd != ASSUAN_INVALID_FD)
{
snprintf (numbuf, sizeof numbuf, "%s%d",
any? " ":"", HANDLE2SOCKET (fi->fd));
any = 1;
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
}
}
else
err = gpg_error (GPG_ERR_ASS_PARAMETER);
return leave_cmd (ctx, err);
}
static const char hlp_shutdown[] =
"SHUTDOWN\n"
"\n"
"Shutdown the server process\n";
static gpg_error_t
cmd_shutdown (assuan_context_t ctx, char *line)
{
(void)line;
shutdown_pending = 1;
return leave_cmd (ctx, 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);
const char * const help;
} table[] =
{
#ifdef HAVE_W32CE_SYSTEM
{ "LS", cmd_ls, hlp_ls },
{ "RUN", cmd_run, hlp_run },
#endif
{ "PWD", cmd_pwd, hlp_pwd },
{ "CD", cmd_cd, hlp_cd },
{ "ECHO", cmd_echo, hlp_echo },
{ "CAT", cmd_cat, hlp_cat },
{ "NEWDATAPORT", cmd_newdataport, hlp_newdataport },
{ "DATAPORT", cmd_dataport, hlp_dataport },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "SHUTDOWN", cmd_shutdown, hlp_shutdown },
{ 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, table[i].help);
if (rc)
return rc;
}
return 0;
}
static assuan_fd_t
get_connection_fd (assuan_context_t ctx)
{
assuan_fd_t fds[5];
if (assuan_get_active_fds (ctx, 0, fds, DIM (fds)) < 1)
log_fatal ("assuan_get_active_fds failed\n");
if (fds[0] == ASSUAN_INVALID_FD)
log_fatal ("assuan_get_active_fds returned invalid conenction fd\n");
return fds[0];
}
/* Startup the server. */
static void
server (void)
{
gpg_error_t err;
assuan_fd_t server_fd;
assuan_sock_nonce_t server_nonce;
int one = 1;
struct sockaddr_in name;
assuan_context_t ctx;
state_t state = NULL;
err = assuan_new (&ctx);
if (err)
log_fatal ("assuan_new failed: %s\n", gpg_strerror (err));
server_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0);
if (server_fd == ASSUAN_INVALID_FD)
log_fatal ("socket() failed: %s\n", strerror (errno));
if (setsockopt (HANDLE2SOCKET (server_fd),
SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof one))
log_error ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
name.sin_family = AF_INET;
name.sin_port = htons (server_port);
name.sin_addr.s_addr = htonl (INADDR_ANY);
if (assuan_sock_bind (server_fd, (struct sockaddr *) &name, sizeof name))
log_fatal ("bind() failed: %s\n", strerror (errno));
if (assuan_sock_get_nonce ((struct sockaddr*)&name, sizeof name,
&server_nonce))
log_fatal ("assuan_sock_get_nonce failed: %s\n", strerror (errno));
/* Register the nonce with the context so that assuan_accept knows
about it. We can't do that directly in assuan_sock_bind because
we want these socket wrappers to be context neutral and drop in
replacement for the standard socket functions. */
assuan_set_sock_nonce (ctx, &server_nonce);
if (listen (HANDLE2SOCKET (server_fd), 5))
log_fatal ("listen() failed: %s\n", strerror (errno));
log_info ("server listening on port %hd\n", server_port);
err = assuan_init_socket_server (ctx, server_fd, 0);
if (err)
log_fatal ("assuan_init_socket_server failed: %s\n", gpg_strerror (err));
err = register_commands (ctx);
if (err)
log_fatal ("register_commands failed: %s\n", gpg_strerror(err));
if (debug)
assuan_set_log_stream (ctx, stderr);
assuan_register_reset_notify (ctx, reset_notify);
assuan_register_input_notify (ctx, input_notify);
assuan_register_output_notify (ctx, output_notify);
state = new_state ();
assuan_set_pointer (ctx, state);
while (!shutdown_pending)
{
int done;
err = assuan_accept (ctx);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF || err == -1)
log_error ("assuan_accept failed: %s\n", gpg_strerror (err));
break;
}
log_info ("client connected. Client's pid is %ld\n",
(long)assuan_get_pid (ctx));
do
{
/* We need to use select here so that we can accept
supplemental connections from the client as requested by
the DATAPORT command. */
fd_set rfds;
int connfd, datafd, max_fd;
connfd = HANDLE2SOCKET (get_connection_fd (ctx));
FD_ZERO (&rfds);
FD_SET (connfd, &rfds);
max_fd = connfd;
if (state->dataport_listen_fd != ASSUAN_INVALID_FD)
{
datafd = HANDLE2SOCKET (state->dataport_listen_fd);
FD_SET (datafd, &rfds);
if (datafd > max_fd)
max_fd = datafd;
}
else
datafd = -1;
if (select (max_fd + 1, &rfds, NULL, NULL, NULL) > 0)
{
if (datafd != -1 && FD_ISSET (datafd, &rfds))
{
struct sockaddr_in clnt_addr;
socklen_t len = sizeof clnt_addr;
int fd;
fd = accept (datafd, (struct sockaddr*)&clnt_addr, &len);
if (fd == -1)
{
err = gpg_err_code_from_syserror ();
assuan_sock_close (state->dataport_listen_fd);
state->dataport_listen_fd = ASSUAN_INVALID_FD;
log_error ("accepting on dataport failed: %s\n",
gpg_strerror (err));
state->dataport_accept_err = err;
err = 0;
}
else
{
/* No more need for the listening socket. */
assuan_sock_close (state->dataport_listen_fd);
state->dataport_listen_fd = ASSUAN_INVALID_FD;
/* Record the accepted fd. */
state->dataport_accept_err = 0;
state->dataport_accepted_fd = SOCKET2HANDLE (fd);
}
}
if (FD_ISSET (connfd, &rfds))
{
err = assuan_process_next (ctx, &done);
}
}
}
while (!err && !done && !shutdown_pending);
if (err)
log_error ("assuan_process failed: %s\n", gpg_strerror (err));
}
assuan_sock_close (server_fd);
assuan_release (ctx);
release_state (state);
}
/*
M A I N
*/
int
main (int argc, char **argv)
{
gpg_error_t err;
int last_argc = -1;
if (argc)
{
log_set_prefix (*argv);
argc--; argv++;
}
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--help"))
{
printf (
"usage: %s [options]\n"
"\n"
"Options:\n"
" --verbose Show what is going on\n",
log_get_prefix ());
exit (0);
}
if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose = debug = 1;
argc--; argv++;
}
}
assuan_set_assuan_log_prefix (log_prefix);
if (debug)
assuan_set_assuan_log_stream (stderr);
err = assuan_sock_init ();
if (err)
log_fatal ("assuan_sock_init failed: %s\n", gpg_strerror (err));
log_info ("server starting...\n");
server ();
log_info ("server finished\n");
assuan_sock_deinit ();
return errorcount ? 1 : 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 7:03 PM (9 h, 10 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
14/9e/a13e0cbf441d15339e04670876a8

Event Timeline