/// <summary>
        /// Expect to receive a call.
        /// </summary>
        /// <param name="timeout">Timeout of expecting a call.</param>
        /// <param name="sessionContext">Session Context of the received call.</param>
        /// <param name="opnum">Operation number of the method invoked.</param>
        /// <returns>Received a byte array of the request stub from a client.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when receive error from server.
        /// </exception>
        public virtual byte[] ExpectCall(
            TimeSpan timeout,
            out RpceServerSessionContext sessionContext,
            out ushort opnum)
        {
            sessionContext = null;

            RpcePdu          receivedPdu = ReceiveAndReassemblePdu(timeout, ref sessionContext);
            RpceCoRequestPdu requestPdu  = receivedPdu as RpceCoRequestPdu;

            if (requestPdu == null)
            {
                throw new InvalidOperationException("Expect request_pdu, but received others.");
            }

            opnum = requestPdu.opnum;

            byte[] stub = requestPdu.stub;
            if (stub == null)
            {
                stub = new byte[0];
            }

            return(stub);
        }
Exemple #2
0
        /// <summary>
        /// Receive and reassemble PDU.
        /// </summary>
        /// <param name="timeout">timeout</param>
        /// <returns>PDU</returns>
        private RpcePdu ReceiveAndReassemblePdu(TimeSpan timeout)
        {
            RpcePdu   receivedPdu   = rpceClient.ExpectPdu(timeout);
            RpceCoPdu receivedCoPdu = receivedPdu as RpceCoPdu;

            if (receivedCoPdu == null)
            {
                return(receivedPdu);
            }

            List <RpceCoPdu> pduList = new List <RpceCoPdu>();

            pduList.Add(receivedCoPdu);

            while ((receivedCoPdu.pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) == 0)
            {
                receivedPdu   = rpceClient.ExpectPdu(timeout);
                receivedCoPdu = receivedPdu as RpceCoPdu;
                if (receivedCoPdu == null)
                {
                    throw new InvalidOperationException("CL PDU received inside a connection.");
                }

                pduList.Add(receivedCoPdu);
            }

            return(RpceUtility.ReassemblePdu(rpceClient.Context, pduList.ToArray()));
        }
 /// <summary>
 /// Initialize a RpceTransportEvent.
 /// </summary>
 /// <param name="type">the type of the occurred event.</param>
 /// <param name="remoteEndpoint">the remote endpoint from which the event occurred.</param>
 /// <param name="localEndpoint">the local endpoint from which the event occurred.</param>
 /// <param name="serverContext">The server context.</param>
 /// <param name="sessionContext">The session context.</param>
 /// <param name="pdu">A received PDU.</param>
 internal RpceTransportEvent(
     EventType type,
     object remoteEndpoint,
     object localEndpoint,
     RpceServerContext serverContext,
     RpceServerSessionContext sessionContext,
     RpcePdu pdu) : base(type, remoteEndpoint, localEndpoint, pdu)
 {
     //because sessionContext might be null when accept a connection, we must pass it in.
     this.serverContext  = serverContext;
     this.sessionContext = sessionContext;
     this.pdu            = pdu;
 }
 /// <summary>
 /// Initialize a RpceTransportEvent.
 /// </summary>
 /// <param name="type">the type of the occurred event.</param>
 /// <param name="remoteEndpoint">the remote endpoint from which the event occurred.</param>
 /// <param name="localEndpoint">the local endpoint from which the event occurred.</param>
 /// <param name="serverContext">The server context.</param>
 /// <param name="sessionContext">The session context.</param>
 /// <param name="pdu">A received PDU.</param>
 internal RpceTransportEvent(
     EventType type,
     object remoteEndpoint,
     object localEndpoint,
     RpceServerContext serverContext,
     RpceServerSessionContext sessionContext,
     RpcePdu pdu)
     : base(type, remoteEndpoint, localEndpoint, pdu)
 {
     //because sessionContext might be null when accept a connection, we must pass it in.
     this.serverContext = serverContext;
     this.sessionContext = sessionContext;
     this.pdu = pdu;
 }
        /// <summary>
        /// Calling this method to be notified when a new RPC connection is coming.
        /// </summary>
        /// <param name="ifMatchFunc">Matching function.</param>
        /// <param name="timeout">Timeout of expecting a connection.</param>
        /// <param name="sessionContext">The sessionContext of binded connection.</param>
        /// <returns>If bind succeeded, return true; otherwise, false.</returns>
        private bool InternalExpectBind(
            RpcIfMatchFunc ifMatchFunc,
            TimeSpan timeout,
            ref RpceServerSessionContext sessionContext)
        {
            RpcePdu       receivedPdu = ReceiveAndReassemblePdu(timeout, ref sessionContext);
            RpceCoBindPdu bindPdu     = receivedPdu as RpceCoBindPdu;

            if (bindPdu == null)
            {
                throw new InvalidOperationException("Expect bind_pdu, but received others.");
            }

            RpcIf rpcIf = new RpcIf(sessionContext.InterfaceId, sessionContext.InterfaceMajorVersion, sessionContext.InterfaceMinorVersion);

            if (!ifMatchFunc(rpcIf))
            {
                // Interface doesn't match, response BindNak
                RpceCoBindNakPdu bindNakPdu =
                    rpceServer.CreateCoBindNakPdu(sessionContext, p_reject_reason_t.REASON_NOT_SPECIFIED, null);
                FragmentAndSendPdu(sessionContext, bindNakPdu);
                return(false);
            }

            RpceCoBindAckPdu bindAckPdu = rpceServer.CreateCoBindAckPdu(sessionContext);

            FragmentAndSendPdu(sessionContext, bindAckPdu);

            while (sessionContext.SecurityContext != null && sessionContext.SecurityContextNeedContinueProcessing)
            {
                receivedPdu = ReceiveAndReassemblePdu(timeout, ref sessionContext);
                RpceCoAlterContextPdu alterContextPdu = receivedPdu as RpceCoAlterContextPdu;
                RpceCoAuth3Pdu        auth3Pdu        = receivedPdu as RpceCoAuth3Pdu;

                if (alterContextPdu != null)
                {
                    RpceCoAlterContextRespPdu alterContextRespPdu =
                        rpceServer.CreateCoAlterContextRespPdu(sessionContext);

                    FragmentAndSendPdu(sessionContext, alterContextRespPdu);
                }
                else if (auth3Pdu != null)
                {
                    //Do nothing
                }
            }

            return(true);
        }
Exemple #6
0
 /// <summary>
 /// Handle the received pdu from server.
 /// </summary>
 /// <param name="rpcePdu">
 /// Received pdu received from server.
 /// </param>
 /// <param name="responseStub">
 /// A byte array of the response stub of a method.
 /// RpceStubDecoder can be used to NDR un-marshal parameters to a byte array.
 /// </param>
 /// <exception cref="InvalidOperationException">
 /// Thrown when receive error from server or RPC connection has not been established.
 /// </exception>
 private void HandleRpcePdu(RpcePdu rpcePdu, out byte[] responseStub)
 {
     if (rpcePdu is RpceCoResponsePdu)
     {
         responseStub = (rpcePdu as RpceCoResponsePdu).stub;
     }
     else if (rpcePdu is RpceCoFaultPdu)
     {
         throw new InvalidOperationException((rpcePdu as RpceCoFaultPdu).status.ToString());
     }
     else
     {
         throw new InvalidOperationException(rpcePdu.GetType().ToString());
     }
 }
Exemple #7
0
        /// <summary>
        /// Parse the identifier of the call from the received pdu from server.
        /// </summary>
        /// <param name="rpcePdu">
        /// Received pdu received from server.
        /// </param>
        /// <exception cref="RpceDisconnectedException">
        /// Thrown when receive shutdown PDU.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when receive error from server.
        /// </exception>
        /// <returns>Return the identifier of the call if success, otherwise throw an exception.</returns>
        private uint ParseRpcePdu(RpcePdu rpcePdu)
        {
            if (rpcePdu is RpceCoResponsePdu)
            {
                return((rpcePdu as RpceCoResponsePdu).call_id);
            }
            else if (rpcePdu is RpceCoFaultPdu)
            {
                return((rpcePdu as RpceCoFaultPdu).call_id);
            }

            if (rpcePdu is RpceCoShutdownPdu)
            {
                throw new RpceDisconnectedException("Shutdown PDU received");
            }

            throw new InvalidOperationException(
                      string.Format("Unexpected packet type received - {0}.",
                                    rpcePdu.GetType().Name));
        }
Exemple #8
0
        /// <summary>
        /// Receive response from RPCE server.
        /// </summary>
        /// <param name="callId">
        /// The identifier of this call.
        /// </param>
        /// <param name="timeout">
        /// Timeout period.
        /// </param>
        /// <param name="responseStub">
        /// A byte array of the response stub of a method.
        /// RpceStubDecoder can be used to NDR un-marshal parameters to a byte array.
        /// </param>
        /// <exception cref="ArgumentException">
        /// Thrown when callId is invalid.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when receive error from server or RPC connection has not been established.
        /// </exception>
        /// <exception cref="RpceDisconnectedException">
        /// Thrown when the connection is disconnected.
        /// </exception>
        /// <exception cref="TimeoutException">
        /// Thrown when timeout to expect the response of the async call.
        /// </exception>
        public virtual void RecvResponse(
            uint callId,
            TimeSpan timeout,
            out byte[] responseStub)
        {
            if (rpceClient.IsDisposed)
            {
                throw new InvalidOperationException("RPC connection has not been established.");
            }
            lock (responseLock)
            {
                if (!outstandingCallIds.Contains(callId))
                {
                    throw new ArgumentException("Invalid call id.");
                }
            }

            if (rpceClient.Context.IsAsynchronous)
            {
                TimeSpan leftTime      = timeout;
                DateTime expiratedTime = DateTime.Now + timeout;
                while (leftTime.CompareTo(new TimeSpan(0)) > 0)
                {
                    lock (responseLock)
                    {
                        // Handle the events added in receiving thread.
                        while (rpceClient.Context.Events.Count > 0)
                        {
                            if (rpceClient.Context.Events[0] is RpcePdu)
                            {
                                RpcePdu rpcePdu = rpceClient.Context.Events[0] as RpcePdu;
                                uint    id      = ParseRpcePdu(rpcePdu);

                                rpceClient.Context.ReceivedPdus.Add(id, rpcePdu);
                                rpceClient.Context.Events.RemoveAt(0);
                            }
                            else
                            {
                                throw rpceClient.Context.Events[0] as Exception;
                            }
                        }

                        RpcePdu receivedPdu;
                        if (rpceClient.Context.ReceivedPdus.TryGetValue(callId, out receivedPdu))
                        {
                            rpceClient.Context.ReceivedPdus.Remove(callId);
                            outstandingCallIds.Remove(callId);

                            HandleRpcePdu(receivedPdu, out responseStub);
                            return;
                        }
                    }
                    System.Threading.Thread.Sleep(10);
                    leftTime = expiratedTime - DateTime.Now;
                }

                throw new TimeoutException(string.Format("RecvResponse timeout to expect the response of the call. call_id: {0}", callId));
            }
            else
            {
                RpcePdu receivedPdu = ReceiveAndReassemblePdu(timeout);
                ParseRpcePdu(receivedPdu);
                HandleRpcePdu(receivedPdu, out responseStub);
            }
        }
Exemple #9
0
        /// <summary>
        /// Connect and bind to a RPCE remote host.
        /// </summary>
        /// <param name="protocolSequence">
        /// A protocol sequence.<para/>
        /// Support ncacn_ip_tcp and ncacn_np only.
        /// </param>
        /// <param name="networkAddress">
        /// A network address of RPCE remote host.
        /// </param>
        /// <param name="endpoint">
        /// An endpoint that its format and content
        /// are associated with the protocol sequence.
        /// </param>
        /// <param name="transportCredential">
        /// If connect by SMB/SMB2, it's the security credential
        /// used by underlayer transport (SMB/SMB2).
        /// If connect by TCP, this parameter is ignored.
        /// </param>
        /// <param name="interfaceId">
        /// A Guid of interface_id that is binding to.
        /// </param>
        /// <param name="interfaceMajorVersion">
        /// interface_major_ver that is binding to.
        /// </param>
        /// <param name="interfaceMinorVersion">
        /// interface_minor_ver that is binding to.
        /// </param>
        /// <param name="securityContext">
        /// A security provider. If setting to null, indicate the default authentication type NTLM is selected.
        /// </param>
        /// <param name="connectSecurityContext">
        /// A security provider for connect authentication. If setting to null, indicate the default authentication type NTLM is selected.
        /// </param>
        /// <param name="authenticationLevel">
        /// An authentication level.
        /// </param>
        /// <param name="supportsHeaderSign">
        /// Indicates whether client supports header sign or not.
        /// </param>
        /// <param name="timeout">
        /// Timeout period.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when protSeq, networkAddr or endpoint is null.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when receive error from server.
        /// </exception>
        public virtual void Bind(
            string protocolSequence,
            string networkAddress,
            string endpoint,
            AccountCredential transportCredential,
            Guid interfaceId,
            ushort interfaceMajorVersion,
            ushort interfaceMinorVersion,
            ClientSecurityContext securityContext,
            ClientSecurityContext connectSecurityContext,
            RpceAuthenticationLevel authenticationLevel,
            bool supportsHeaderSign,
            TimeSpan timeout)
        {
            if (protocolSequence == null)
            {
                throw new ArgumentNullException("protocolSequence");
            }
            if (networkAddress == null)
            {
                throw new ArgumentNullException("networkAddress");
            }
            if (endpoint == null)
            {
                throw new ArgumentNullException("endpoint");
            }

            // RPC over named-pipe does not support asynchronous call in this library.
            // http://msdn.microsoft.com/en-us/library/windows/desktop/aa373551(v=vs.85).aspx
            rpceClient.Context.IsAsynchronous = (string.Compare(protocolSequence, RpceUtility.RPC_OVER_NAMED_PIPE_PROTOCOL_SEQUENCE, true) != 0);

            rpceClient.Connect(protocolSequence, networkAddress, endpoint, transportCredential, timeout,
                               connectSecurityContext == null ? SecurityPackageType.Ntlm : connectSecurityContext.PackageType);

            rpceClient.SetAuthInfo(securityContext, authenticationLevel, rpceClient.Context.AuthenticationContextId);

            RpceCoBindPdu bindPdu = rpceClient.CreateCoBindPdu(
                //read from context, donot hardcode.
                rpceClient.Context.RpcVersionMinor,         // default is rpc vers 5.0
                supportsHeaderSign ? RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN : RpceCoPfcFlags.None,
                rpceClient.ComputeNextCallId(),             // call id, default is 1
                rpceClient.Context.MaxTransmitFragmentSize, // max xmit frag
                rpceClient.Context.MaxReceiveFragmentSize,  // max recv frag
                rpceClient.Context.AssociateGroupId,        // assoc group id, default 0
                interfaceId,
                interfaceMajorVersion,
                interfaceMinorVersion,
                rpceClient.Context.NdrVersion,                                                                       // default is NDR
                (rpceClient.Context.BindTimeFeatureNegotiationBitmask != RpceBindTimeFeatureNegotiationBitmask.None) // default is None
                    ? (RpceBindTimeFeatureNegotiationBitmask?)rpceClient.Context.BindTimeFeatureNegotiationBitmask
                    : null);

            FragmentAndSendPdu(bindPdu);

            RpcePdu receivedPdu = ReceiveAndReassemblePdu(timeout);

            if (receivedPdu is RpceCoBindAckPdu)
            {
                if (rpceClient.Context.NdrVersion == RpceNdrVersion.None)
                {
                    throw new InvalidOperationException("Neither NDR nor NDR64 is supported.");
                }
            }
            else
            {
                RpceCoBindNakPdu bindNakPdu = receivedPdu as RpceCoBindNakPdu;
                if (bindNakPdu != null)
                {
                    throw new InvalidOperationException(bindNakPdu.provider_reject_reason.ToString());
                }
                else
                {
                    throw new InvalidOperationException(
                              string.Format("Unexpected packet type received - {0}.",
                                            receivedPdu.GetType().Name));
                }
            }

            while (rpceClient.Context.SecurityContext != null &&
                   rpceClient.Context.SecurityContext.NeedContinueProcessing)
            {
                RpceCoAlterContextPdu alterContextPdu = rpceClient.CreateCoAlterContextPdu();
                FragmentAndSendPdu(alterContextPdu);
                receivedPdu = ReceiveAndReassemblePdu(timeout);

                RpceCoFaultPdu faultPdu = receivedPdu as RpceCoFaultPdu;
                if (faultPdu != null)
                {
                    throw new InvalidOperationException(faultPdu.status.ToString());
                }

                if (!(receivedPdu is RpceCoAlterContextRespPdu))
                {
                    throw new InvalidOperationException("Expect alter_context_pdu, but received others.");
                }
            }

            if (rpceClient.Context.SecurityContext != null &&
                rpceClient.Context.SecurityContext.Token != null &&
                rpceClient.Context.SecurityContext.Token.Length != 0)
            {
                RpceCoAuth3Pdu auth3Pdu = rpceClient.CreateCoAuth3Pdu();
                FragmentAndSendPdu(auth3Pdu);
                // no expected response from server
                rpceClient.Context.OutstandingCalls.Remove(auth3Pdu.call_id);
            }

            if (rpceClient.Context.IsAsynchronous)
            {
                // Start the receiving thread to receive the response from server.
                cancellationToken = new CancellationTokenSource();
                receiveTask       = new Task(EventLoop, cancellationToken.Token); //new Thread(new ThreadStart(EventLoop));
                //receiveThread.IsBackground = true;
                receiveTask.Start();
            }
        }
        /// <summary>
        /// update context on receiving PDU.
        /// </summary>
        /// <param name="pdu">PDU to receive.</param>
        /// <exception cref="ArgumentNullException">Thrown when pdu is null.</exception>
        internal void UpdateContextOnReceivingPdu(RpcePdu pdu)
        {
            RpceCoPdu coPdu = pdu as RpceCoPdu;
            if (coPdu == null)
            {
                return;
            }

            this.RpcVersionMajor = coPdu.rpc_vers;
            this.RpcVersionMinor = coPdu.rpc_vers_minor;
            if (coPdu.PTYPE == RpcePacketType.Bind ||
                coPdu.PTYPE == RpcePacketType.BindAck)
            {
                //TDI:
                //TD said PFC_SUPPORT_HEADER_SIGN is meanful in Bind and AltContext.
                //But when using Kerberos, AltContext doesn't have this flag,
                //if server or client read this flag according to AltContext, Sign/Encrypt will fail.
                this.SupportsHeaderSign
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN)
                            == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN;
                this.SupportsConcurrentMultiplexing
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX)
                            == RpceCoPfcFlags.PFC_CONC_MPX;
            }

            this.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat;
            this.CurrentCallId = coPdu.call_id;
            if (coPdu.PTYPE != RpcePacketType.Response && coPdu.PTYPE != RpcePacketType.Auth3)
            {
                this.outstandingCalls.Add(coPdu.call_id);
            }

            switch (coPdu.PTYPE)
            {
                case RpcePacketType.Bind:
                    RpceCoBindPdu bindPdu = coPdu as RpceCoBindPdu;
                    if (bindPdu != null)
                    {
                        UpdateContextOnReceivingBindPdu(bindPdu);
                    }
                    break;

                case RpcePacketType.Request:
                    RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu;
                    if (requestPdu != null)
                    {
                        this.ContextIdentifier = requestPdu.p_cont_id;
                    }
                    break;

                case RpcePacketType.Response:
                    RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu;
                    if (responsePdu != null)
                    {
                        this.ContextIdentifier = responsePdu.p_cont_id;
                    }
                    break;

                case RpcePacketType.Auth3:
                case RpcePacketType.AlterContext:
                case RpcePacketType.CoCancel:
                case RpcePacketType.Orphaned:
                default:
                    //default situation should do nothing.
                    //This is just update the context, if we cannot recognize the PDU, ignore it.
                    break;
            }

            this.SecurityContextNeedContinueProcessing = coPdu.securityContextNeedContinueProcessing;
        }
Exemple #11
0
        /// <summary>
        /// update context on receiving PDU.
        /// </summary>
        /// <param name="pdu">PDU to receive.</param>
        /// <exception cref="ArgumentNullException">Thrown when pdu is null.</exception>
        internal void UpdateContextOnReceivingPdu(RpcePdu pdu)
        {
            RpceCoPdu coPdu = pdu as RpceCoPdu;

            if (coPdu == null)
            {
                return;
            }

            this.RpcVersionMajor = coPdu.rpc_vers;
            this.RpcVersionMinor = coPdu.rpc_vers_minor;
            if (coPdu.PTYPE == RpcePacketType.Bind ||
                coPdu.PTYPE == RpcePacketType.BindAck)
            {
                //TDI:
                //TD said PFC_SUPPORT_HEADER_SIGN is meanful in Bind and AltContext.
                //But when using Kerberos, AltContext doesn't have this flag,
                //if server or client read this flag according to AltContext, Sign/Encrypt will fail.
                this.SupportsHeaderSign
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN)
                      == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN;
                this.SupportsConcurrentMultiplexing
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX)
                      == RpceCoPfcFlags.PFC_CONC_MPX;
            }

            this.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat;
            this.CurrentCallId = coPdu.call_id;
            if (coPdu.PTYPE != RpcePacketType.Response && coPdu.PTYPE != RpcePacketType.Auth3)
            {
                this.outstandingCalls.Add(coPdu.call_id);
            }

            switch (coPdu.PTYPE)
            {
            case RpcePacketType.Bind:
                RpceCoBindPdu bindPdu = coPdu as RpceCoBindPdu;
                if (bindPdu != null)
                {
                    UpdateContextOnReceivingBindPdu(bindPdu);
                }
                break;

            case RpcePacketType.Request:
                RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu;
                if (requestPdu != null)
                {
                    this.ContextIdentifier = requestPdu.p_cont_id;
                }
                break;

            case RpcePacketType.Response:
                RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu;
                if (responsePdu != null)
                {
                    this.ContextIdentifier = responsePdu.p_cont_id;
                }
                break;

            case RpcePacketType.Auth3:
            case RpcePacketType.AlterContext:
            case RpcePacketType.CoCancel:
            case RpcePacketType.Orphaned:
            default:
                //default situation should do nothing.
                //This is just update the context, if we cannot recognize the PDU, ignore it.
                break;
            }

            this.SecurityContextNeedContinueProcessing = coPdu.securityContextNeedContinueProcessing;
        }
        /// <summary>
        /// Parse the identifier of the call from the received pdu from server.
        /// </summary>
        /// <param name="rpcePdu">
        /// Received pdu received from server.
        /// </param>
        /// <exception cref="RpceDisconnectedException">
        /// Thrown when receive shutdown PDU.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when receive error from server.
        /// </exception>
        /// <returns>Return the identifier of the call if success, otherwise throw an exception.</returns>
        private uint ParseRpcePdu(RpcePdu rpcePdu)
        {
            if (rpcePdu is RpceCoResponsePdu)
            {
                return (rpcePdu as RpceCoResponsePdu).call_id;
            }
            else if (rpcePdu is RpceCoFaultPdu)
            {
                return (rpcePdu as RpceCoFaultPdu).call_id;
            }

            if (rpcePdu is RpceCoShutdownPdu)
            {
                throw new RpceDisconnectedException("Shutdown PDU received");
            }

            throw new InvalidOperationException(
                string.Format("Unexpected packet type received - {0}.",
                rpcePdu.GetType().Name));
        }
        /// <summary>
        /// Send a PDU to a specific connected client.
        /// </summary>
        /// <param name="pdu">Rpce PDU to send.</param>
        /// <param name="sessionContext">Context of the RPCE session.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when sessionContext or pdu is null.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when underlayer transport was not established.
        /// Thrown when protocol sequence is unknown.
        /// </exception>
        public virtual void SendPdu(
            RpceServerSessionContext sessionContext,
            RpcePdu pdu)
        {
            if (sessionContext == null)
            {
                throw new ArgumentNullException("sessionContext");
            }

            if (pdu == null)
            {
                throw new ArgumentNullException("pdu");
            }

            EnsureTransportIsValid();

            sessionContext.UpdateContextOnSendingPdu(pdu);

            if (sessionContext.ProtocolSequence.Equals(
                RpceUtility.RPC_OVER_TCPIP_PROTOCOL_SEQUENCE,
                StringComparison.OrdinalIgnoreCase))
            {
                this.tcpTransport.SendPacket(sessionContext.RemoteEndpoint, pdu);
            }
            else if (sessionContext.ProtocolSequence.Equals(
                RpceUtility.RPC_OVER_NAMED_PIPE_PROTOCOL_SEQUENCE,
                StringComparison.OrdinalIgnoreCase))
            {
                lock (sessionContext.smbSendingLocker)
                {
                    sessionContext.smbBufferSending = ArrayUtility.ConcatenateArrays(
                        sessionContext.smbBufferSending,
                        pdu.ToBytes());

                    if (sessionContext.smbSendingFunc != null)
                    {
                        sessionContext.smbSendingFunc();
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Unknown protocol sequence.");
            }
        }
        /// <summary>
        /// Expect an event (connect, disconnect, pdu received) with the specific sessionContext.
        /// </summary>
        /// <param name="timeout">Timeout of expecting a PDU.</param>
        /// <param name="sessionContext">
        /// Session context to receive the event.
        /// Null means expect event on all sessions.
        /// </param>
        /// <param name="pdu">The PDU if receiving a packet; otherwise, null.</param>
        /// <returns>The expected PDU. It is null when event is connect or disconnect.</returns>
        /// <returns>The event type.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when server was not started.
        /// </exception>
        public virtual EventType ExpectEvent(
            TimeSpan timeout,
            ref RpceServerSessionContext sessionContext,
            out RpcePdu pdu)
        {
            EnsureTransportIsValid();

            RpceEventFilter filter;
            if (sessionContext == null)
            {
                filter = new RpceEventFilter(
                EventType.Connected | EventType.ReceivedPacket | EventType.Disconnected);
            }
            else
            {
                filter = new RpceEventFilter(
                EventType.Connected | EventType.ReceivedPacket | EventType.Disconnected,
                    sessionContext.RemoteEndpoint);
            }

            RpceTransportEvent transportEvent =
                this.receivedTransportEvents.Dequeue(timeout, filter.EventFilter);
            ValidateTransportEvent(
                transportEvent,
                EventType.Connected | EventType.ReceivedPacket | EventType.Disconnected);

            if (transportEvent.EventType == EventType.Connected)
            {
                pdu = null;
                RpceServerContext serverContext = transportEvent.ServerContext;
                sessionContext = serverContext.CreateAndAddSessionContext(
                    transportEvent.RemoteEndPoint);
                return EventType.Connected;
            }

            sessionContext = transportEvent.SessionContext;
            if (sessionContext == null)
            {
                throw new InvalidOperationException(
                    "Session context was not found for this disconnect event.");
            }

            if (transportEvent.EventType == EventType.ReceivedPacket)
            {
                pdu = transportEvent.Pdu;
                if (pdu == null)
                {
                    throw new InvalidOperationException("Unknown object received from transport.");
                }
                sessionContext.UpdateContextOnReceivingPdu(pdu);

                return EventType.ReceivedPacket;
            }

            if (transportEvent.EventType == EventType.Disconnected)
            {
                pdu = null;
                transportEvent.ServerContext.RemoveSessionContext(sessionContext);
                return EventType.Disconnected;
            }

            throw new InvalidOperationException("unknown event type");
        }
        /// <summary>
        /// Encode a PDU to a binary stream. Then send the stream.
        /// The PDU can be got by calling method Create***Pdu.
        /// </summary>
        /// <param name="pdu">
        /// A specified type of a PDU. This argument can not be null.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when pdu parameter passed to the method is null.
        /// </exception>
        public void SendPdu(RpcePdu pdu)
        {
            if (pdu == null)
            {
                throw new ArgumentNullException("pdu");
            }

            UpdateContextOnSendingPdu(pdu);

            if (context.tcpTransport != null)
            {
                context.tcpTransport.SendPacket(pdu);
            }
            else if (context.fileServiceTransport != null)
            {
                RpceCoPdu coPdu = pdu as RpceCoPdu;
                if (coPdu != null)
                {
                    switch (coPdu.PTYPE)
                    {
                        case RpcePacketType.Request:
                        case RpcePacketType.Response:
                            if (context.fileServiceTransport is SmbClientTransport)
                            {
                                // SMB doesn't support FSCTL_PIPE_TRANSCEIVE.
                                // This is the last fragment of a request for synchronous RPCs.
                                if (context.UseTransactionForNamedPipe && !context.IsAsynchronous && ((coPdu.pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) != 0))
                                {
                                    TransactionOverWriteAndRead(pdu);
                                }
                                else
                                {
                                    context.fileServiceTransport.Write(timeoutForFsTransport, 0, pdu.ToBytes());
                                }
                            }
                            else if ((coPdu.pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) == 0)
                            {
                                // This is one of fragment PDU, but not the last.
                                context.fileServiceTransport.Write(timeoutForFsTransport, 0, pdu.ToBytes());
                            }
                            else
                            {
                                // This is the only PDU of a request,
                                // or this is the last fragment of a request.
                                byte[] inputResponse;
                                byte[] outputResponse;
                                context.fileServiceTransport.IoControl(
                                    timeoutForFsTransport,
                                    FsCtlCode.FSCTL_PIPE_TRANSCEIVE,
                                    pdu.ToBytes(),
                                    out inputResponse,
                                    out outputResponse,
                                    0,
                                    context.MaxOutputResponse);
                                if (outputResponse == null)
                                {
                                    throw new InvalidOperationException(
                                        "Got an error in SMB/SMB2 transport. SMB/SMB2 should throw exception.");
                                }
                                pipeTransceiveResponseQueue.Enqueue(outputResponse);
                            }
                            break;
                        case RpcePacketType.Bind:
                        case RpcePacketType.AlterContext:
                            if (context.UseTransactionForNamedPipe && (context.fileServiceTransport is SmbClientTransport) && !context.IsAsynchronous)
                            {
                                TransactionOverWriteAndRead(pdu);
                            }
                            else
                            {
                                context.fileServiceTransport.Write(timeoutForFsTransport, 0, pdu.ToBytes());
                            }
                            break;
                        case RpcePacketType.Auth3:
                        case RpcePacketType.CoCancel:
                        case RpcePacketType.Orphaned:
                        default:
                            context.fileServiceTransport.Write(timeoutForFsTransport, 0, pdu.ToBytes());
                            break;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("No connection was established.");
            }
        }
        /// <summary>
        /// update context on sending PDU
        /// </summary>
        /// <param name="pdu">PDU</param>
        private void UpdateContextOnSendingPdu(RpcePdu pdu)
        {
            RpceCoPdu coPdu = pdu as RpceCoPdu;
            if (coPdu == null)
            {
                return;
            }

            context.RpcVersionMajor = coPdu.rpc_vers;
            context.RpcVersionMinor = coPdu.rpc_vers_minor;
            if (coPdu.PTYPE == RpcePacketType.Bind ||
                coPdu.PTYPE == RpcePacketType.BindAck ||
                coPdu.PTYPE == RpcePacketType.AlterContext ||
                coPdu.PTYPE == RpcePacketType.AlterContextResp)
            {
                context.SupportsHeaderSign
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN)
                            == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN;
            }
            context.SupportsConcurrentMultiplexing
                = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX)
                        == RpceCoPfcFlags.PFC_CONC_MPX;
            context.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat;
            context.OutstandingCalls.Add(coPdu.call_id);

            switch (coPdu.PTYPE)
            {
                case RpcePacketType.Bind:
                    RpceCoBindPdu bindPdu = coPdu as RpceCoBindPdu;
                    if (bindPdu != null)
                    {
                        UpdateContextOnSendingBindPdu(bindPdu);
                    }
                    break;

                case RpcePacketType.AlterContext:
                    break;

                case RpcePacketType.Auth3:
                    break;

                case RpcePacketType.Request:
                    RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu;
                    if (requestPdu != null)
                    {
                        context.ContextIdentifier = requestPdu.p_cont_id;
                    }
                    break;

                case RpcePacketType.Response:
                case RpcePacketType.CoCancel:
                case RpcePacketType.Orphaned:
                default:
                    //default situation should do nothing.
                    //This is just update the context, if we cannot recognize the PDU, ignore it.
                    break;
            }
        }
        /// <summary>
        /// update context on receiving PDU
        /// </summary>
        /// <param name="pdu">PDU</param>
        private void UpdateContextOnReceivingPdu(RpcePdu pdu)
        {
            RpceCoPdu coPdu = pdu as RpceCoPdu;
            if (coPdu == null)
            {
                return;
            }

            context.RpcVersionMajor = coPdu.rpc_vers;
            context.RpcVersionMinor = coPdu.rpc_vers_minor;
            if (coPdu.PTYPE == RpcePacketType.Bind ||
                coPdu.PTYPE == RpcePacketType.BindAck)
            {
                context.SupportsHeaderSign
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN)
                            == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN;
                context.SupportsConcurrentMultiplexing
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX)
                            == RpceCoPfcFlags.PFC_CONC_MPX;
            }

            context.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat;
            context.OutstandingCalls.Remove(coPdu.call_id);

            switch (coPdu.PTYPE)
            {
                case RpcePacketType.BindAck:
                    RpceCoBindAckPdu bindAckPdu = coPdu as RpceCoBindAckPdu;
                    if (bindAckPdu != null)
                    {
                        UpdateContextOnReceivingBindAckPdu(bindAckPdu);
                    }
                    break;

                case RpcePacketType.Response:
                    RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu;
                    if (responsePdu != null)
                    {
                        context.ContextIdentifier = responsePdu.p_cont_id;
                    }
                    break;

                case RpcePacketType.BindNak:
                case RpcePacketType.AlterContextResp:
                case RpcePacketType.Request:
                case RpcePacketType.Fault:
                case RpcePacketType.Shutdown:
                default:
                    //default situation should do nothing.
                    //This is just update the context, if we cannot recognize the PDU, ignore it.
                    break;
            }

            context.SecurityContextNeedContinueProcessing = coPdu.securityContextNeedContinueProcessing;
        }
 /// <summary>
 /// MS-RPCE Section 2.1.1.2: In the case of synchronous RPCs, an implementation of these extensions MAY require the Server Message Block (SMB) Protocol implementation 
 /// to execute a transaction encompassing the write of the last request PDU and the read of the first response PDU on the client.     
 /// The last request PDU MUST be a bind, an alter_context, or the last fragment of a request. 
 /// The first response PDU MUST be a bind_ack or bind_nak, an alter_context_response, or the first fragment of a response.
 /// The transaction over a write and read is as specified in [MS-CIFS].
 /// Windows always asks the Server Message Block implementation to execute a transaction on the client for synchronous RPCs.
 /// </summary>
 /// <param name="pdu">A specified type of a PDU. This argument can not be null.</param>
 private void TransactionOverWriteAndRead(RpcePdu pdu)
 {
     byte[] readData;
     SmbClientTransport smbClientTransport = context.fileServiceTransport as SmbClientTransport;
     smbClientTransport.Transaction(timeoutForFsTransport, pdu.ToBytes(), out readData);
     if (readData == null)
     {
         throw new InvalidOperationException(
             "Got an error in SMB/SMB2 transport. SMB/SMB2 should throw exception.");
     }
     pipeTransceiveResponseQueue.Enqueue(readData);
 }
Exemple #19
0
        /// <summary>
        /// update context on sending PDU.
        /// </summary>
        /// <param name="pdu">PDU to send.</param>
        internal void UpdateContextOnSendingPdu(RpcePdu pdu)
        {
            RpceCoPdu coPdu = pdu as RpceCoPdu;

            if (coPdu == null)
            {
                return;
            }

            this.RpcVersionMajor = coPdu.rpc_vers;
            this.RpcVersionMinor = coPdu.rpc_vers_minor;
            if (coPdu.PTYPE == RpcePacketType.Bind ||
                coPdu.PTYPE == RpcePacketType.BindAck ||
                coPdu.PTYPE == RpcePacketType.AlterContext ||
                coPdu.PTYPE == RpcePacketType.AlterContextResp)
            {
                this.SupportsHeaderSign
                    = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN)
                      == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN;
            }

            this.SupportsConcurrentMultiplexing
                = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX)
                  == RpceCoPfcFlags.PFC_CONC_MPX;
            this.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat;

            if (coPdu.PTYPE != RpcePacketType.Request)
            {
                this.outstandingCalls.Remove(coPdu.call_id);
            }

            switch (coPdu.PTYPE)
            {
            case RpcePacketType.BindAck:
                RpceCoBindAckPdu bindAckPdu = coPdu as RpceCoBindAckPdu;
                if (bindAckPdu != null)
                {
                    UpdateContextOnSendingBindAckPdu(bindAckPdu);
                }
                break;

            case RpcePacketType.Request:
                RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu;
                if (requestPdu != null)
                {
                    this.ContextIdentifier = requestPdu.p_cont_id;
                }
                break;

            case RpcePacketType.Response:
                RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu;
                if (responsePdu != null)
                {
                    this.ContextIdentifier = responsePdu.p_cont_id;
                }
                break;

            case RpcePacketType.BindNak:
            case RpcePacketType.AlterContextResp:
            case RpcePacketType.Fault:
            case RpcePacketType.Shutdown:
            default:
                //default situation should do nothing.
                //This is just update the context, if we cannot recognize the PDU, ignore it.
                break;
            }
        }
 /// <summary>
 /// Handle the received pdu from server.
 /// </summary>
 /// <param name="rpcePdu">
 /// Received pdu received from server.
 /// </param>
 /// <param name="responseStub">
 /// A byte array of the response stub of a method.
 /// RpceStubDecoder can be used to NDR un-marshal parameters to a byte array.
 /// </param>
 /// <exception cref="InvalidOperationException">
 /// Thrown when receive error from server or RPC connection has not been established.
 /// </exception>
 private void HandleRpcePdu(RpcePdu rpcePdu, out byte[] responseStub)
 {
     if (rpcePdu is RpceCoResponsePdu)
     {
         responseStub = (rpcePdu as RpceCoResponsePdu).stub;
     }
     else if (rpcePdu is RpceCoFaultPdu)
     {
         throw new InvalidOperationException((rpcePdu as RpceCoFaultPdu).status.ToString());
     }
     else
     {
         throw new InvalidOperationException(rpcePdu.GetType().ToString());
     }
 }