singrdk/base/Services/Smb/Client/Transactor.sg

297 lines
10 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Services/Smb/Client/Transactor.sg
//
// Note:
//
// A "transactor" is anything that can issue requests (transactions)
// against the transport mux. These requests are issued using the
// SmbTransactor contract (in Services/Smb/PrivateContracts). The
// TransportMux thread holds the export side of the contract, and
// various other threads (DirectoryClient and FileExp service threads)
// hold the import sides.
//
// The Transactor class contains state information for each SmbTransactor
// channel, for the TransportMux class. The transport mux runs on a
// single thread; only that thread creates and uses instances of the
// Transactor class. So there is no need to synchronize access to these
// instances.
//
// TODO:
//
// * Implement fragmentation for TRANSACTION2 requests. Reassembly
// for inbound fragments is implemented, but not for outbound requests.
//
/*
REQUESTS vs TRANSACTIONS
------------------------
At its most basic, SMB provides a way to identify message boundaries (framing),
and a common header for all messages exchanged. Most of those messages are
request/response messages, where a single message sent from the client to the
server causes the server to response with exactly one message. We will refer
to these as "SMB requests", although that's a really horribly vague term.
There are some SMB protocols that do not conform to this usage pattern.
Notably, SMB supports "transactions", which are identified by the SMB commands
SMB_TRANSACTION and SMB_TRANSACTION2. We will only deal with SMB_TRANSACTION2.
These transactions add several fields to the basic request/response header,
and they also allow the payload of the transaction to span more than one SMB
message.
First, the client sends an SMB message to the server, identifying the SMB
transaction. There are two possibilities to consider: First, the client's
transaction message may be small enough to fit into a single SMB message;
this is the easy case. Otherwise, the client must fragment its transaction
request. In either case, the client sends its first SMB message, with
the Command field of the SMB header set to SMB_TRANSACTION2.
Then the client waits for a response from the server. If the client needed
to fragment its transaction request, then it does NOT yet send any further
fragments. The server then decides whether or not it likes the request.
If the primary request indicates that the client needs to send more fragments,
then the server will respond with the "Interim Server Response". This is
just a "go ahead" message (which means one more useless round-trip).
The client then sends all of its fragments.
The server then sends its transaction responses. If the response does not
need to be fragmented, then there will only be a single response message.
*/
using System;
using System.Text;
using System.Collections;
using System.Threading;
using System.Text;
using System.Security.Protocols.Ntlm;
using System.Runtime.InteropServices;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using NetStack.Contracts;
using Smb.Protocol;
using Smb.PublicChannels;
using Smb.PrivateChannels;
using Smb.Shared;
namespace Smb.Client
{
/// <summary>
/// This class represents the mux's view of a transactor. Only the mux thread creates or manipulates
/// instances of this class. And because only a single thread ever touches these instances, locking
/// is not required.
/// </summary>
internal class Transactor
{
public Transactor(MuxTuple tuple)
{
this.Tuple = tuple;
this.State = TransactorState.Ready;
this.DebugPrefix = "Tran" + tuple.ToString() + ": ";
TrackedData data = new TrackedData();
this._datacon = new TContainer<TrackedData>(data);
}
public class TrackedData : ITracked
{
// This buffer can be null.
// This buffer is used to reassemble fragments of a TRANSACTION2 request.
public byte[] in ExHeap TransactionResponseBuffer;
public int TransactionResponseFragmentByteIndex;
public int TransactionRequestFragmentByteIndex;
public byte[] in ExHeap ResponseReassemblyBuffer;
public int Transaction_ReceivedParameterBytes;
public int Transaction_ReceivedDataBytes;
public int Transaction_ReceivedFragmentCount;
public int Transaction_DataOffset;
public int Transaction_ParameterOffset;
// In the "Ready" state, this field is null.
// In all other states, this field is non-null.
public SmbTransactor.Exp Exp;
// Contains a reference to the byte[] in ExHeap buffer that contains a message that
// needs to be transmitted to the remote SMB peer. A transactor client (a holder of
// SmbTransactor.Imp) submits these buffers by sending the Request message.
public byte[] in ExHeap RequestBuffer;
// The length of the data in _buffer. Right now, this is always equal to the length
// of the buffer itself, because the current TcpConnectionContract does not allow
// for sends that are shorter than the length of the buffer.
public int RequestLength;
public byte[] in ExHeap ExtractResponseReassemblyBuffer()
{
expose(this)
{
byte[] in ExHeap buffer = this.ResponseReassemblyBuffer;
this.ResponseReassemblyBuffer = null;
return buffer;
}
}
public SmbTransactor.Exp! AcquireEndpoint()
{
expose (this) {
SmbTransactor.Exp exp = this.Exp;
this.Exp = null;
assert exp != null;
return exp;
}
}
#if false
void ITracked.Acquire() {}
void ITracked.Release() {}
void ITracked.Expose() {}
void ITracked.UnExpose() {}
#else
public void Acquire() {}
public void Release() {}
public void Expose() {}
public void UnExpose() {}
#endif
public void Dispose()
{
delete TransactionResponseBuffer;
delete RequestBuffer;
delete Exp;
// SelfReference.Dispose();
// delete SelfReference;
delete ResponseReassemblyBuffer;
}
}
readonly TContainer<TrackedData>! _datacon;
public TrackedData! AcquireData()
{
return _datacon.Acquire();
}
public void ReleaseData([Claims]TrackedData! data)
{
_datacon.Release(data);
}
public SmbTransactor.Exp! AcquireEndpoint()
{
TrackedData! tracked = AcquireData();
SmbTransactor.Exp exp = tracked.AcquireEndpoint();
ReleaseData(tracked);
assert exp != null;
return exp;
}
public byte[] in ExHeap ExtractResponseReassemblyBuffer()
{
TrackedData! tracked = AcquireData();
byte[] in ExHeap buffer;
expose(tracked)
{
buffer = tracked.ResponseReassemblyBuffer;
tracked.ResponseReassemblyBuffer = null;
ReleaseData(tracked);
}
return buffer;
}
public readonly string! DebugPrefix;
public readonly MuxTuple Tuple;
public TransactorState State;
public bool InQueue;
public SmbFlag1 _smbFlag1;
public SmbFlag2 _smbFlag2;
public SmbCommand Command;
public void Dispose()
{
TrackedData data = AcquireData();
data.Dispose();
}
override public string! ToString()
{
string! stateText = StateToString(this.State);
return String.Format("[Transactor tuple {0} state {1}]", this.Tuple.ToString(), stateText);
}
public static string! StateToString(TransactorState tstate)
{
switch (tstate)
{
case TransactorState.Ready: return "Ready";
case TransactorState.WaitingSendRequest: return "WaitingSendRequest";
case TransactorState.WaitingSendPrimaryTransactionRequest: return "WaitingSendPrimaryTransactionRequest";
case TransactorState.WaitingSendSecondaryTransactionRequest: return "WaitingSendSecondaryTransactionRequest";
case TransactorState.WaitingReceiveResponse: return "WaitingReceiveResponse";
case TransactorState.WaitingReceivePrimaryTransactionResponse: return "WaitingReceivePrimaryTransactionResponse";
case TransactorState.WaitingReceiveSecondaryTransactionResponse: return "WaitingReceiveSecondaryTransactionResponse";
default: return "???" + ((int)tstate).ToString();
}
}
}
enum TransactorState
{
// In this state, the ExpRef has been acquired (so don't try to acquire it again), and the exp is in the
// endpoint map that is owned by the multiplexer thread. Two things can happen to this transactor:
//
// * The transactor can close its channel. In this case, the multiplexer thread removes the
// transactor state from the _transactors hash table.
//
// * The transactor can start a new request. The multiplexer thread releases the ExpRef, and
// sets Transactor.State to Queued.
Ready,
// In this state, the multiplexer has received a request from the transactor client (the imp side).
// The multiplexer has not yet transmitted the packet to the remote peer.
//
// _waitingResponseExp contains a valid pointer.
// _buffer contains a valid pointer.
//
// Events:
//
// * If the TCP connection closes or fails, then the multiplexer thread will:
// acquire _waitingResponseExp, send its a NakRequest, and then delete it.
// acquire _buffer and delete it.
// remove the Transactor from the _transactors hash table.
//
// * When the multiplexer transmits the message, it removes the
WaitingSendRequest,
// In this state, the multiplexer has transmitted the request to the remote peer, and is now waiting
// for a response.
//
// * If the TCP connection closes or fails, then the multiplexer thread will acquire the
// waiting response endpoint, will send a NakRequest, will close the channel, and then
// will remove the tuple entry from the table.
//
WaitingReceiveResponse,
WaitingSendPrimaryTransactionRequest,
WaitingReceiveInterimTransactionResponse,
WaitingSendSecondaryTransactionRequest,
WaitingReceivePrimaryTransactionResponse,
WaitingReceiveSecondaryTransactionResponse,
}
}