/* * Windows support module which deals with being a named-pipe server. */ #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" typedef struct NamedPipeServerSocket { /* 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; struct handle *callback_handle; /* winhandl.c's reference */ /* PuTTY Socket machinery */ Plug *plug; char *error; Socket sock; } NamedPipeServerSocket; static Plug *sk_namedpipeserver_plug(Socket *s, Plug *p) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); Plug *ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_namedpipeserver_close(Socket *s) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); if (ps->callback_handle) handle_free(ps->callback_handle); 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) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); return ps->error; } static SocketPeerInfo *sk_namedpipeserver_peer_info(Socket *s) { return NULL; } static bool create_named_pipe(NamedPipeServerSocket *ps, bool 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, NULL, plug, true); } static void named_pipe_accept_loop(NamedPipeServerSocket *ps, bool 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) { NamedPipeServerSocket *ps = (NamedPipeServerSocket *)vps; named_pipe_accept_loop(ps, true); } /* * This socket type is only used for listening, so it should never * be asked to write or set_frozen. */ static const SocketVtable NamedPipeServerSocket_sockvt = { .plug = sk_namedpipeserver_plug, .close = sk_namedpipeserver_close, .socket_error = sk_namedpipeserver_socket_error, .peer_info = sk_namedpipeserver_peer_info, }; Socket *new_named_pipe_listener(const char *pipename, Plug *plug) { NamedPipeServerSocket *ret = snew(NamedPipeServerSocket); ret->sock.vt = &NamedPipeServerSocket_sockvt; ret->plug = plug; ret->error = NULL; ret->psd = NULL; ret->pipename = dupstr(pipename); ret->acl = NULL; ret->callback_handle = 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); ret->callback_handle = handle_add_foreign_event(ret->connect_ovl.hEvent, named_pipe_connect_callback, ret); named_pipe_accept_loop(ret, false); cleanup: return &ret->sock; } #endif /* !defined NO_SECURITY */