diff options
Diffstat (limited to 'winsup/cygwin/cygserver_transport_pipes.cc')
-rwxr-xr-x | winsup/cygwin/cygserver_transport_pipes.cc | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc new file mode 100755 index 000000000..6d80defd4 --- /dev/null +++ b/winsup/cygwin/cygserver_transport_pipes.cc @@ -0,0 +1,362 @@ +/* cygserver_transport_pipes.cc + + Copyright 2001, 2002 Red Hat Inc. + + Written by Robert Collins <rbtcollins@hotmail.com> + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* to allow this to link into cygwin and the .dll, a little magic is needed. */ +#ifdef __OUTSIDE_CYGWIN__ +#include "woutsup.h" +#else +#include "winsup.h" +#endif + +#include <sys/types.h> + +#include <assert.h> +#include <netdb.h> +#include <pthread.h> +#include <unistd.h> + +#include "cygerrno.h" +#include "cygwin/cygserver_transport.h" +#include "cygwin/cygserver_transport_pipes.h" + +#ifndef __INSIDE_CYGWIN__ +#include "cygwin/cygserver.h" +#endif + +enum + { + MAX_WAIT_NAMED_PIPE_RETRY = 64, + WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds + }; + +#ifndef __INSIDE_CYGWIN__ + +static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT; +static CRITICAL_SECTION pipe_instance_lock; +static long pipe_instance = 0; + +static void +initialise_pipe_instance_lock () +{ + assert (pipe_instance == 0); + InitializeCriticalSection (&pipe_instance_lock); +} + +#endif /* !__INSIDE_CYGWIN__ */ + +#ifndef __INSIDE_CYGWIN__ + +transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe) + : _pipe_name (""), + _hPipe (hPipe), + _is_accepted_endpoint (true), + _is_listening_endpoint (false) +{ + assert (_hPipe); + assert (_hPipe != INVALID_HANDLE_VALUE); + + init_security (); +} + +#endif /* !__INSIDE_CYGWIN__ */ + +transport_layer_pipes::transport_layer_pipes () + : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"), + _hPipe (NULL), + _is_accepted_endpoint (false), + _is_listening_endpoint (false) +{ + init_security (); +} + +void +transport_layer_pipes::init_security () +{ + assert (wincap.has_security ()); + + /* FIXME: pthread_once or equivalent needed */ + + InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE); + + _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); + _sec_all_nih.lpSecurityDescriptor = &_sd; + _sec_all_nih.bInheritHandle = FALSE; +} + +transport_layer_pipes::~transport_layer_pipes () +{ + close (); +} + +#ifndef __INSIDE_CYGWIN__ + +int +transport_layer_pipes::listen () +{ + assert (!_hPipe); + assert (!_is_accepted_endpoint); + assert (!_is_listening_endpoint); + + _is_listening_endpoint = true; + + /* no-op */ + return 0; +} + +class transport_layer_pipes * +transport_layer_pipes::accept (bool *const recoverable) +{ + assert (!_hPipe); + assert (!_is_accepted_endpoint); + assert (_is_listening_endpoint); + + pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock); + + EnterCriticalSection (&pipe_instance_lock); + + // Read: http://www.securityinternals.com/research/papers/namedpipe.php + // See also the Microsoft security bulletins MS00-053 and MS01-031. + + // FIXME: Remove FILE_CREATE_PIPE_INSTANCE. + + const bool first_instance = (pipe_instance == 0); + + const HANDLE accept_pipe = + CreateNamedPipe (_pipe_name, + (PIPE_ACCESS_DUPLEX + | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)), + (PIPE_TYPE_BYTE | PIPE_WAIT), + PIPE_UNLIMITED_INSTANCES, + 0, 0, 1000, + &_sec_all_nih); + + const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE + && pipe_instance == 0 + && GetLastError () == ERROR_ACCESS_DENIED); + + if (accept_pipe != INVALID_HANDLE_VALUE) + InterlockedIncrement (&pipe_instance); + + LeaveCriticalSection (&pipe_instance_lock); + + if (duplicate) + { + *recoverable = false; + system_printf ("failed to create named pipe: " + "is the daemon already running?"); + return NULL; + } + + if (accept_pipe == INVALID_HANDLE_VALUE) + { + debug_printf ("error creating pipe (%lu).", GetLastError ()); + *recoverable = true; // FIXME: case analysis? + return NULL; + } + + assert (accept_pipe); + + if (!ConnectNamedPipe (accept_pipe, NULL) + && GetLastError () != ERROR_PIPE_CONNECTED) + { + debug_printf ("error connecting to pipe (%lu)", GetLastError ()); + (void) CloseHandle (accept_pipe); + *recoverable = true; // FIXME: case analysis? + return NULL; + } + + return safe_new (transport_layer_pipes, accept_pipe); +} + +#endif /* !__INSIDE_CYGWIN__ */ + +void +transport_layer_pipes::close () +{ + // verbose: debug_printf ("closing pipe %p", _hPipe); + + if (_hPipe) + { + assert (_hPipe != INVALID_HANDLE_VALUE); + +#ifndef __INSIDE_CYGWIN__ + + if (_is_accepted_endpoint) + { + (void) FlushFileBuffers (_hPipe); // Blocks until client reads. + (void) DisconnectNamedPipe (_hPipe); + EnterCriticalSection (&pipe_instance_lock); + (void) CloseHandle (_hPipe); + assert (pipe_instance > 0); + InterlockedDecrement (&pipe_instance); + LeaveCriticalSection (&pipe_instance_lock); + } + else + (void) CloseHandle (_hPipe); + +#else /* __INSIDE_CYGWIN__ */ + + assert (!_is_accepted_endpoint); + (void) ForceCloseHandle (_hPipe); + +#endif /* __INSIDE_CYGWIN__ */ + + _hPipe = NULL; + } +} + +ssize_t +transport_layer_pipes::read (void *const buf, const size_t len) +{ + // verbose: debug_printf ("reading from pipe %p", _hPipe); + + assert (_hPipe); + assert (_hPipe != INVALID_HANDLE_VALUE); + assert (!_is_listening_endpoint); + + DWORD count; + if (!ReadFile (_hPipe, buf, len, &count, NULL)) + { + debug_printf ("error reading from pipe (%lu)", GetLastError ()); + set_errno (EINVAL); // FIXME? + return -1; + } + + return count; +} + +ssize_t +transport_layer_pipes::write (void *const buf, const size_t len) +{ + // verbose: debug_printf ("writing to pipe %p", _hPipe); + + assert (_hPipe); + assert (_hPipe != INVALID_HANDLE_VALUE); + assert (!_is_listening_endpoint); + + DWORD count; + if (!WriteFile (_hPipe, buf, len, &count, NULL)) + { + debug_printf ("error writing to pipe, error = %lu", GetLastError ()); + set_errno (EINVAL); // FIXME? + return -1; + } + + return count; +} + +/* + * This routine holds a static variable, assume_cygserver, that is set + * if the transport has good reason to think that cygserver is + * running, i.e. if if successfully connected to it with the previous + * attempt. If this is set, the code tries a lot harder to get a + * connection, making the assumption that any failures are just + * congestion and overloading problems. + */ + +int +transport_layer_pipes::connect () +{ + assert (!_hPipe); + assert (!_is_accepted_endpoint); + assert (!_is_listening_endpoint); + + static bool assume_cygserver = false; + + BOOL rc = TRUE; + int retries = 0; + + while (rc) + { + _hPipe = CreateFile (_pipe_name, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &_sec_all_nih, + OPEN_EXISTING, + SECURITY_IMPERSONATION, + NULL); + + if (_hPipe != INVALID_HANDLE_VALUE) + { + assert (_hPipe); +#ifdef __INSIDE_CYGWIN__ + ProtectHandle (_hPipe); +#endif + assume_cygserver = true; + return 0; + } + + _hPipe = NULL; + + if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY) + { + debug_printf ("Error opening the pipe (%lu)", GetLastError ()); + return -1; + } + + /* Note: `If no instances of the specified named pipe exist, the + * WaitNamedPipe function returns immediately, regardless of the + * time-out value.' Thus the explicit Sleep if the call fails + * with ERROR_FILE_NOT_FOUND. + */ + while (retries != MAX_WAIT_NAMED_PIPE_RETRY + && !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT))) + { + if (GetLastError () == ERROR_FILE_NOT_FOUND) + Sleep (0); // Give the server a chance. + + retries += 1; + } + } + + assert (retries == MAX_WAIT_NAMED_PIPE_RETRY); + + system_printf ("lost connection to cygserver, error = %lu", + GetLastError ()); + + assume_cygserver = false; + + return -1; +} + +#ifndef __INSIDE_CYGWIN__ + +void +transport_layer_pipes::impersonate_client () +{ + assert (_hPipe); + assert (_hPipe != INVALID_HANDLE_VALUE); + assert (_is_accepted_endpoint); + + // verbose: debug_printf ("impersonating pipe %p", _hPipe); + if (_hPipe) + { + assert (_hPipe != INVALID_HANDLE_VALUE); + + if (!ImpersonateNamedPipeClient (_hPipe)) + debug_printf ("Failed to Impersonate the client, (%lu)", + GetLastError ()); + } + // verbose: debug_printf ("I am who you are"); +} + +void +transport_layer_pipes::revert_to_self () +{ + assert (_is_accepted_endpoint); + + RevertToSelf (); + // verbose: debug_printf ("I am who I yam"); +} + +#endif /* !__INSIDE_CYGWIN__ */ |