/// <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); }
/// <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); }
/// <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()); } }
/// <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> /// 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); } }
/// <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; }
/// <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); }
/// <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()); } }