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