Пример #1
0
        /// <summary>
        /// Attempts to connect to the specified process.
        /// </summary>
        private NamedPipeClientStream TryConnectToProcess(int nodeProcessId, int timeout, long hostHandshake, long clientHandshake)
        {
            // Try and connect to the process.
            string pipeName = "MSBuild" + nodeProcessId;

            NamedPipeClientStream nodeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
            CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId, pipeName, timeout);

            try
            {
                nodeStream.Connect(timeout);

                // Verify that the owner of the pipe is us.  This prevents a security hole where a remote node has
                // been faked up with ACLs that would let us attach to it.  It could then issue fake build requests back to
                // us, potentially causing us to execute builds that do harmful or unexpected things.  The pipe owner can
                // only be set to the user's own SID by a normal, unprivileged process.  The conditions where a faked up
                // remote node could set the owner to something else would also let it change owners on other objects, so
                // this would be a security flaw upstream of us.
                SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner;
                PipeSecurity remoteSecurity = nodeStream.GetAccessControl();
                IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier));
                if (remoteOwner != identifier)
                {
                    CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value);
                    throw new UnauthorizedAccessException();
                }

                CommunicationsUtilities.Trace("Writing handshake to pipe {0}", pipeName);
#if true
                nodeStream.WriteLongForHandshake(hostHandshake);
#else
                // When the 4th and subsequent node start up, we see this taking a long period of time (0.5s or greater.)  This is strictly for debugging purposes.
                DateTime writeStart = DateTime.UtcNow;
                nodeStream.WriteLong(HostHandshake);
                DateTime writeEnd = DateTime.UtcNow;
                Console.WriteLine("Node ProcessId {0} WriteLong {1}", nodeProcessId, (writeEnd - writeStart).TotalSeconds);
#endif
                CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName);
                long handshake = nodeStream.ReadLongForHandshake();

                if (handshake != clientHandshake)
                {
                    CommunicationsUtilities.Trace("Handshake failed. Received {0} from client not {1}. Probably the client is a different MSBuild build.", handshake, clientHandshake);
                    throw new InvalidOperationException();
                }

                // We got a connection.
                CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName);
                return nodeStream;
            }
            catch (Exception e)
            {
                if (ExceptionHandling.IsCriticalException(e))
                {
                    throw;
                }

                // Can be:
                // UnauthorizedAccessException -- Couldn't connect, might not be a node.
                // IOException -- Couldn't connect, already in use.
                // TimeoutException -- Couldn't connect, might not be a node.
                // InvalidOperationException – Couldn’t connect, probably a different build
                CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd());

                // If we don't close any stream, we might hang up the child
                if (nodeStream != null)
                {
                    nodeStream.Close();
                }
            }

            return null;
        }