Ejemplo n.º 1
0
        /// <summary>
        /// Sends a response to a previously received request over the stream
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="writer">The writer instance.</param>
        /// <param name="requestId">The request ID.</param>
        /// <param name="command">The command to respond to.</param>
        /// <param name="exception">The exception to respond with.</param>
        public static async Task WriteErrorResponseAsync(BinaryConverterStream writer, long requestId, Command command, Exception exception)
        {
            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{requestId} - Sending error response to: {command}");

            await writer.WriteUInt8Async((byte)MessageType.Error);

            await writer.WriteUInt8Async((byte)command);

            await writer.WriteInt64Async(requestId);

            await writer.WriteExceptionAsync(exception);

            await writer.FlushAsync();
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Handles a request on this instance
        /// </summary>
        /// <param name="client">The socket to transfer.</param>
        /// <param name="remoteEndPoint">The remote endpoint</param>
        /// <param name="logtaskid">The log task ID</param>
        public async Task HandleRequest(long client, EndPoint remoteEndPoint, string logtaskid)
        {
            Program.DebugConsoleOutput("Processing new request with native handle");

            // On POSIX systems we use SCM_RIGHTS
            if (SystemHelper.IsCurrentOSPosix)
            {
                try
                {
                    // Prepare a stream
                    using (var ms = new System.IO.MemoryStream())
                        using (var bcs = new BinaryConverterStream(ms, m_fdSocketTypeSerializer))
                        {
                            // Serialize the request
                            await bcs.WriteObjectAsync(new SocketRequest()
                            {
                                Handle     = client,
                                RemoteIP   = ((IPEndPoint)remoteEndPoint).Address.ToString(),
                                RemotePort = ((IPEndPoint)remoteEndPoint).Port,
                                LogTaskID  = logtaskid
                            });

                            await bcs.FlushAsync();

                            Program.DebugConsoleOutput($"Sending socket {client} with {ms.Length} bytes of data to {m_proc.Id}");

                            // Send the request data with the handle to the remote process
                            using (await m_lock.LockAsync())
                                SockRock.ScmRightsImplementation.send_fds(m_fdSocket.Handle.ToInt32(), new int[] { (int)client }, ms.ToArray());

                            Program.DebugConsoleOutput($"Data sent to {m_proc.Id}, closing local socket");

                            // Make sure the handle is no longer in this process
                            SockRock.ScmRightsImplementation.native_close((int)client);

                            Program.DebugConsoleOutput($"Completed sending the socket to {m_proc.Id}");
                        }
                }
                catch (Exception ex)
                {
                    Program.ConsoleOutput("Failed to handle request: {0}", ex);
                }
            }
            else
            {
                throw new PlatformNotSupportedException($"Unable to transmit the socket on the current platform: {SystemHelper.CurrentOS}");
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Sends the initial descriptor to the file descriptor socket
        /// </summary>
        /// <returns>An awaitable task.</returns>
        private async Task SetupDescriptorSocket()
        {
            using (var ms = new System.IO.MemoryStream())
                using (var bcs = new BinaryConverterStream(ms, m_fdSocketTypeSerializer))
                {
                    await bcs.WriteObjectAsync(new InitialProtocolDescription()
                    {
                        Version          = 1,
                        ServerHandle     = ((IRemoteInstance)m_proxy).Handle,
                        RequestSignature = m_fdSocketTypeSerializer.GetShortTypeDefinition(typeof(SocketRequest))
                    });

                    await bcs.FlushAsync();

                    m_fdSocket.Send(ms.ToArray());

                    Program.DebugConsoleOutput($"Sent protocol data with {ms.Length} bytes to {m_proc.Id}");
                }
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:LeanIPC.IPCPeer"/> class.
 /// </summary>
 /// <param name="reader">The stream to read from.</param>
 /// <param name="reader">The stream to write to.</param>
 /// <param name="authenticationHandler">The authentication code to use</param>
 public IPCPeer(BinaryConverterStream reader, BinaryConverterStream writer, IAuthenticationHandler authenticationHandler = null)
 {
     m_connection = new InterProcessConnection(reader, writer, authenticationHandler);
 }
Ejemplo n.º 5
0
 /// <summary>
 /// Sends a response to a previously received request over the stream
 /// </summary>
 /// <returns>An awaitable task.</returns>
 /// <param name="writer">The writer instance.</param>
 /// <param name="requestId">The request ID.</param>
 /// <param name="command">The command to respond to.</param>
 /// <param name="arguments">The arguments in the response.</param>
 public static Task WriteResponseAsync(BinaryConverterStream writer, long requestId, Command command, Type[] types, object[] arguments)
 {
     return(WriteReqOrRespAsync(writer, MessageType.Response, requestId, command, types, arguments));
 }
Ejemplo n.º 6
0
 /// <summary>
 /// Sends a request over the stream
 /// </summary>
 /// <returns>An awaitable task.</returns>
 /// <param name="writer">The writer instance.</param>
 /// <param name="requestId">The request ID.</param>
 /// <param name="command">The command to send.</param>
 /// <param name="argument">The value to send.</param>
 /// <typeparam name="T">The data type parameter.</typeparam>
 public static Task WriteRequestAsync <T>(BinaryConverterStream writer, long requestId, Command command, T argument)
 {
     return(WriteReqOrRespAsync(writer, MessageType.Request, requestId, command, new Type[] { typeof(T) }, new object[] { argument }));
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Reads a request or a repsonse from the stream
        /// </summary>
        /// <returns>The parsed message.</returns>
        /// <param name="reader">The reader instances.</param>
        public static async Task <ParsedMessage> ReadRequestOrResponseAsync(BinaryConverterStream reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"Reading header");

            MessageType type;

            try
            {
                type = (MessageType)await reader.ReadUInt8Async();
            }
            catch (EndOfStreamException)
            {
                // If we get EoS during the header, we are cleanly terminated
                throw new ConnectionClosedException();
            }

            var command = (Command)await reader.ReadUInt8Async();

            var id = await reader.ReadInt64Async();

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{id} - Read header for: {type}-{command}");

            Type[] types;
            if (type == MessageType.Error)
            {
                types = new Type[] { typeof(Exception) };
            }
            else
            {
                System.Diagnostics.Trace.WriteLineIf(TRACE, $"{id} - Reading type header for {type}-{command}");
                var typestr = await reader.ReadStringAsync();

                System.Diagnostics.Trace.WriteLineIf(TRACE, $"{id} - Parsing type header for {type}-{command}: {typestr}");
                types = reader.TypeSerializer.ParseShortTypeDefinition(typestr);
            }


            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{id} - Reading {types} arguments for: {type}-{command}");
            var arguments = await reader.ReadRecordAsync(types);

            if (type != MessageType.Error)
            {
                switch (command)
                {
                case Command.Ready:
                case Command.Ping:
                case Command.Shutdown:
                case Command.DetachRemoteObject:
                case Command.RegisterRemoteObject:
                case Command.InvokeRemoteMethod:
                    // Re-construct the message structure from the fields
                    types     = new Type[] { (type == MessageType.Request || type == MessageType.Passthrough) ? GetRequestMessageType(command) : GetResponseMessageType(command) };
                    arguments = new object[] { reader.TypeSerializer.DeserializeObject(types[0], arguments) };
                    break;

                case Command.UserData:
                    break;

                default:
                    throw new ArgumentException($"The value {command} is not a supported command type");
                }
            }

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{id} - Returning message for: {type}-{command}");

            return(new ParsedMessage(type, id, command, arguments, types, type == MessageType.Error ? (Exception)arguments[0] : null));
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Sends a request or response over the stream
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="writer">The writer instance.</param>
        /// <param name="messageType">The message type</param>
        /// <param name="requestId">The request ID.</param>
        /// <param name="command">The command to send.</param>
        /// <param name="types">The types of the arguments to send.</param>
        /// <param name="arguments">The arguments to send.</param>
        private static async Task WriteReqOrRespAsync(BinaryConverterStream writer, MessageType messageType, long requestId, Command command, Type[] types, object[] arguments)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            types     = types ?? new Type[0];
            arguments = arguments ?? new object[0];
            if (types.Length != arguments.Length)
            {
                throw new ArgumentException($"The {nameof(types)} array has length {types.Length} and does not match the {nameof(arguments)} array, which has {arguments.Length}", nameof(types));
            }

            if (messageType == MessageType.Error)
            {
                if (types.Length != 1 || !typeof(Exception).IsAssignableFrom(types[0]))
                {
                    throw new ArgumentException("When writing an error response, the only argument must be the exception");
                }

                await WriteErrorResponseAsync(writer, requestId, command, (Exception)arguments[0]);

                return;
            }

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{requestId} - Writing header for: {messageType}-{command}");

            await writer.WriteUInt8Async((byte)messageType);

            await writer.WriteUInt8Async(((byte)command));

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{requestId} - Writing ID header for: {messageType}-{command}");

            await writer.WriteInt64Async(requestId);

            switch (command)
            {
            case Command.Ready:
            case Command.Ping:
            case Command.Shutdown:
            case Command.DetachRemoteObject:
            case Command.RegisterRemoteObject:
            case Command.InvokeRemoteMethod:
                var targettype = messageType != MessageType.Response ? GetRequestMessageType(command) : GetResponseMessageType(command);

                if (types.Length != 1 || types[0] != targettype)
                {
                    throw new ArgumentException($"The {command} command can only be sent with a single {targettype} argument");
                }

                //We rewire the arguments to fit the well-known layout, such that we transmit only the fields, not the struct itself
                var tmp = writer.TypeSerializer.SerializeObject(arguments[0], targettype);
                types     = tmp.Item2;
                arguments = tmp.Item3;

                if (targettype == typeof(InvokeRemoteMethodResponse))
                {
                    types[1] = (Type)arguments[0];
                }

                break;

            case Command.UserData:
                break;

            default:
                throw new ArgumentException($"The value {command} is not a supported command type");
            }

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{requestId} - Writing record for: {messageType}-{command}");
            await writer.WriteRecordAsync(arguments, types, true);

            System.Diagnostics.Trace.WriteLineIf(TRACE, $"{requestId} - Wrote record for: {messageType}-{command}");

            await writer.FlushAsync();
        }
Ejemplo n.º 9
0
        private static async Task ListenForRequests(RPCPeer peer, string prefix, string path)
        {
            Program.DebugConsoleOutput($"Setting up listener ...");

            var tp = new TypeSerializer(false, false);

            var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);

            socket.Connect(new SockRock.UnixEndPoint(prefix + path + "_fd"));

            Program.DebugConsoleOutput($"Connected to fd socket, reading initial data");

            SpawnedServer server;

            // Start by reading and verifying the protocol intial data
            var buffer = new byte[1024];
            var count  = socket.Receive(buffer);

            Program.DebugConsoleOutput($"Got protocol data with {count} bytes");

            using (var ms = new System.IO.MemoryStream(buffer, 0, count, false))
                using (var bcs = new BinaryConverterStream(ms, tp, false))
                {
                    var desc = await bcs.ReadAnyAsync <InitialProtocolDescription>();

                    if (desc.Version != 1)
                    {
                        throw new Exception($"Expected protocol version 1, but got {desc.Version}");
                    }
                    if (desc.RequestSignature != tp.GetShortTypeName(typeof(SocketRequest)))
                    {
                        throw new Exception($"Expected type name to be {tp.GetShortTypeName(typeof(SocketRequest))}, but it was {desc.RequestSignature}");
                    }
                    if (!peer.RemoteHandler.TryGetLocalObject(desc.ServerHandle, out var obj) || obj == null)
                    {
                        throw new Exception($"Unable to find the instance with the given handle: {desc.ServerHandle}");
                    }
                    server = obj as SpawnedServer;
                    if (server == null)
                    {
                        throw new Exception($"Unable to find the instance with the given handle: {desc.ServerHandle}, got something that is not a server ...");
                    }
                }

            Program.DebugConsoleOutput($"Protocol verification completed, starting main loop");

            // Prepare the handle
            var rchandle = socket.Handle.ToInt32();

            try
            {
                // Use a single allocated buffer for all requests
                using (var ms = new System.IO.MemoryStream())
                    using (var bcs = new BinaryConverterStream(ms, tp, false))
                    {
                        Program.DebugConsoleOutput("{0} Entering main loop", System.Diagnostics.Process.GetCurrentProcess().Id);
                        while (socket.Connected)
                        {
                            try
                            {
                                // Get the next request from the socket
                                Program.DebugConsoleOutput("{0} Getting file handle", System.Diagnostics.Process.GetCurrentProcess().Id);
                                var req = SockRock.ScmRightsImplementation.recv_fds(rchandle);
                                if (req == null)
                                {
                                    Program.DebugConsoleOutput("{0} Socket closed", System.Diagnostics.Process.GetCurrentProcess().Id);
                                    break;
                                }

                                Program.DebugConsoleOutput("{0} Got request, parsing ...", System.Diagnostics.Process.GetCurrentProcess().Id);

                                // Copy the buffer into the stream we read from
                                ms.Position = 0;
                                ms.Write(req.Item2, 0, req.Item2.Length);
                                ms.Position = 0;

                                // Extract the data
                                var data = await bcs.ReadAnyAsync <SocketRequest>();

                                Program.DebugConsoleOutput("{0}, Decoded request, local handle is {1} remote handle is {2}", System.Diagnostics.Process.GetCurrentProcess().Id, req.Item1[0], data.Handle);

                                // All set, fire the request
                                Task.Run(
                                    () => server.HandleRequestSimple(req.Item1[0], new IPEndPoint(IPAddress.Parse(data.RemoteIP), data.RemotePort), data.LogTaskID)
                                    ).ContinueWith(
                                    _ => Program.DebugConsoleOutput("{0}: Request handling completed", System.Diagnostics.Process.GetCurrentProcess().Id)
                                    );
                            }
                            catch (Exception ex)
                            {
                                Program.ConsoleOutput("{0}: Processing failed: {1}", System.Diagnostics.Process.GetCurrentProcess().Id, ex);
                                return;
                            }
                        }
                    }
            }
            finally
            {
                var st = DateTime.Now;
                Program.DebugConsoleOutput("{0}: Stopping spawned process", System.Diagnostics.Process.GetCurrentProcess().Id);
                server.Stop(TimeSpan.FromMinutes(5));
                Program.DebugConsoleOutput("{0}: Stopped spawned process in {1}", System.Diagnostics.Process.GetCurrentProcess().Id, DateTime.Now - st);
                peer.Dispose();
                Program.DebugConsoleOutput("{0}: Stopped peer in {1}", System.Diagnostics.Process.GetCurrentProcess().Id, DateTime.Now - st);
            }
        }