aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/plink/winnps.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/tools/plink/winnps.c b/tools/plink/winnps.c
new file mode 100644
index 000000000..a172628f8
--- /dev/null
+++ b/tools/plink/winnps.c
@@ -0,0 +1,248 @@
+/*
+ * Windows support module which deals with being a named-pipe server.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#define DEFINE_PLUG_METHOD_MACROS
+#include "tree234.h"
+#include "putty.h"
+#include "network.h"
+#include "proxy.h"
+#include "ssh.h"
+
+#if !defined NO_SECURITY
+
+#include "winsecur.h"
+
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
+ int overlapped);
+
+typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket;
+struct Socket_named_pipe_server_tag {
+ const struct socket_function_table *fn;
+ /* the above variable absolutely *must* be the first in this structure */
+
+ /* Parameters for (repeated) creation of named pipe objects */
+ PSECURITY_DESCRIPTOR psd;
+ PACL acl;
+ char *pipename;
+
+ /* The current named pipe object + attempt to connect to it */
+ HANDLE pipehandle;
+ OVERLAPPED connect_ovl;
+
+ /* PuTTY Socket machinery */
+ Plug plug;
+ char *error;
+};
+
+static Plug sk_namedpipeserver_plug(Socket s, Plug p)
+{
+ Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
+ Plug ret = ps->plug;
+ if (p)
+ ps->plug = p;
+ return ret;
+}
+
+static void sk_namedpipeserver_close(Socket s)
+{
+ Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
+
+ CloseHandle(ps->pipehandle);
+ CloseHandle(ps->connect_ovl.hEvent);
+ sfree(ps->error);
+ sfree(ps->pipename);
+ if (ps->acl)
+ LocalFree(ps->acl);
+ if (ps->psd)
+ LocalFree(ps->psd);
+ sfree(ps);
+}
+
+static const char *sk_namedpipeserver_socket_error(Socket s)
+{
+ Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
+ return ps->error;
+}
+
+static int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance)
+{
+ SECURITY_ATTRIBUTES sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = ps->psd;
+ sa.bInheritHandle = FALSE;
+
+ ps->pipehandle = CreateNamedPipe
+ (/* lpName */
+ ps->pipename,
+
+ /* dwOpenMode */
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_OVERLAPPED |
+ (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
+
+ /* dwPipeMode */
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ | PIPE_REJECT_REMOTE_CLIENTS
+#endif
+ ,
+
+ /* nMaxInstances */
+ PIPE_UNLIMITED_INSTANCES,
+
+ /* nOutBufferSize, nInBufferSize */
+ 4096, 4096, /* FIXME: think harder about buffer sizes? */
+
+ /* nDefaultTimeOut */
+ 0 /* default timeout */,
+
+ /* lpSecurityAttributes */
+ &sa);
+
+ return ps->pipehandle != INVALID_HANDLE_VALUE;
+}
+
+static Socket named_pipe_accept(accept_ctx_t ctx, Plug plug)
+{
+ HANDLE conn = (HANDLE)ctx.p;
+
+ return make_handle_socket(conn, conn, plug, TRUE);
+}
+
+/*
+ * Dummy SockAddr type which just holds a named pipe address. Only
+ * used for calling plug_log from named_pipe_accept_loop() here.
+ */
+SockAddr sk_namedpipe_addr(const char *pipename);
+
+static void named_pipe_accept_loop(Named_Pipe_Server_Socket ps,
+ int got_one_already)
+{
+ while (1) {
+ int error;
+ char *errmsg;
+
+ if (got_one_already) {
+ /* If we were called with a connection already waiting,
+ * skip this step. */
+ got_one_already = FALSE;
+ error = 0;
+ } else {
+ /*
+ * Call ConnectNamedPipe, which might succeed or might
+ * tell us that an overlapped operation is in progress and
+ * we should wait for our event object.
+ */
+ if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl))
+ error = 0;
+ else
+ error = GetLastError();
+
+ if (error == ERROR_IO_PENDING)
+ return;
+ }
+
+ if (error == 0 || error == ERROR_PIPE_CONNECTED) {
+ /*
+ * We've successfully retrieved an incoming connection, so
+ * ps->pipehandle now refers to that connection. So
+ * convert that handle into a separate connection-type
+ * Socket, and create a fresh one to be the new listening
+ * pipe.
+ */
+ HANDLE conn = ps->pipehandle;
+ accept_ctx_t actx;
+
+ actx.p = (void *)conn;
+ if (plug_accepting(ps->plug, named_pipe_accept, actx)) {
+ /*
+ * If the plug didn't want the connection, might as
+ * well close this handle.
+ */
+ CloseHandle(conn);
+ }
+
+ if (!create_named_pipe(ps, FALSE)) {
+ error = GetLastError();
+ } else {
+ /*
+ * Go round again to see if more connections can be
+ * got, or to begin waiting on the event object.
+ */
+ continue;
+ }
+ }
+
+ errmsg = dupprintf("Error while listening to named pipe: %s",
+ win_strerror(error));
+ plug_log(ps->plug, 1, sk_namedpipe_addr(ps->pipename), 0,
+ errmsg, error);
+ sfree(errmsg);
+ break;
+ }
+}
+
+static void named_pipe_connect_callback(void *vps)
+{
+ Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket)vps;
+ named_pipe_accept_loop(ps, TRUE);
+}
+
+Socket new_named_pipe_listener(const char *pipename, Plug plug)
+{
+ /*
+ * This socket type is only used for listening, so it should never
+ * be asked to write or flush or set_frozen.
+ */
+ static const struct socket_function_table socket_fn_table = {
+ sk_namedpipeserver_plug,
+ sk_namedpipeserver_close,
+ NULL /* write */,
+ NULL /* write_oob */,
+ NULL /* write_eof */,
+ NULL /* flush */,
+ NULL /* set_frozen */,
+ sk_namedpipeserver_socket_error
+ };
+
+ Named_Pipe_Server_Socket ret;
+
+ ret = snew(struct Socket_named_pipe_server_tag);
+ ret->fn = &socket_fn_table;
+ ret->plug = plug;
+ ret->error = NULL;
+ ret->psd = NULL;
+ ret->pipename = dupstr(pipename);
+ ret->acl = NULL;
+
+ assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0);
+ assert(strchr(pipename + 9, '\\') == NULL);
+
+ if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE,
+ &ret->psd, &ret->acl, &ret->error)) {
+ goto cleanup;
+ }
+
+ if (!create_named_pipe(ret, TRUE)) {
+ ret->error = dupprintf("unable to create named pipe '%s': %s",
+ pipename, win_strerror(GetLastError()));
+ goto cleanup;
+ }
+
+ memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl));
+ ret->connect_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ handle_add_foreign_event(ret->connect_ovl.hEvent,
+ named_pipe_connect_callback, ret);
+ named_pipe_accept_loop(ret, FALSE);
+
+ cleanup:
+ return (Socket) ret;
+}
+
+#endif /* !defined NO_SECURITY */