aboutsummaryrefslogtreecommitdiffstats
path: root/tests/ce-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ce-server.c')
-rw-r--r--tests/ce-server.c811
1 files changed, 781 insertions, 30 deletions
diff --git a/tests/ce-server.c b/tests/ce-server.c
index 1b242aa..6ff3cae 100644
--- a/tests/ce-server.c
+++ b/tests/ce-server.c
@@ -39,10 +39,12 @@
#include <errno.h>
#ifdef HAVE_W32CE_SYSTEM
-#ifndef FILE_ATTRIBUTE_ROMSTATICREF
-#define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE
-#endif
-#endif
+# 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"
@@ -54,26 +56,201 @@ 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
{
- char *cwd; /* The current working directory - access using get_cwd(). */
+ /* 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. */
+/* 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)
{
@@ -88,7 +265,7 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err)
log_error ("command '%s' failed: %s <%s>\n", name,
gpg_strerror (err), gpg_strsource (err));
}
- return err;
+ return assuan_process_done (ctx, err);
}
@@ -167,7 +344,9 @@ get_cwd (state_t state)
{
/* 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;
@@ -197,10 +376,63 @@ get_cwd (state_t state)
}
+
+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[] =
@@ -218,6 +450,116 @@ cmd_echo (assuan_context_t ctx, char *line)
}
+
+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"
@@ -374,12 +716,25 @@ 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)
@@ -399,19 +754,77 @@ cmd_run (assuan_context_t ctx, char *line)
xfree (tmp2);
xfree (tmp1);
}
- if (!CreateProcess (pgmname, /* Program to start. */
- cmdline, /* Command line arguments. */
- NULL, /* Process security attributes. notsup. */
- NULL, /* Thread security attributes. notsup. */
- FALSE, /* Inherit handles. notsup. */
- CREATE_SUSPENDED, /* Creation flags. */
- NULL, /* Environment. notsup. */
- NULL, /* Use current drive/directory. notsup. */
- NULL, /* Startup information. notsup. */
- &pi /* Returns process information. */
- ))
- {
- log_error ("CreateProcess failed: %d", GetLastError ());
+
+ /* 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;
}
@@ -460,6 +873,24 @@ cmd_run (assuan_context_t ctx, char *line)
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);
@@ -467,17 +898,254 @@ cmd_run (assuan_context_t ctx, char *line)
#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 after ending this connection\n";
+ "Shutdown the server process\n";
static gpg_error_t
cmd_shutdown (assuan_context_t ctx, char *line)
{
- (void)ctx;
(void)line;
shutdown_pending = 1;
- return 0;
+ return leave_cmd (ctx, 0);;
}
@@ -498,8 +1166,12 @@ register_commands (assuan_context_t ctx)
{ "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 }
};
@@ -517,6 +1189,20 @@ register_commands (assuan_context_t ctx)
}
+
+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)
@@ -535,20 +1221,20 @@ server (void)
server_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0);
if (server_fd == ASSUAN_INVALID_FD)
- log_fatal ("socket() failed: %s", strerror (errno));
+ 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", strerror (errno));
+ 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", strerror (errno));
+ 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", strerror (errno));
+ 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
@@ -572,12 +1258,19 @@ server (void)
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 ();
- state = xcalloc (1, sizeof state);
assuan_set_pointer (ctx, state);
while (!shutdown_pending)
{
+ int done;
+
err = assuan_accept (ctx);
if (err)
{
@@ -588,8 +1281,66 @@ server (void)
log_info ("client connected. Client's pid is %ld\n",
(long)assuan_get_pid (ctx));
-
- err = assuan_process (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));
}