Skip to content

Update NegotiateStream with async code #4073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,174 +14,142 @@ namespace Examples.NegotiateStreamExample
public class AsynchronousAuthenticatingTcpListener
{
public static void Main()
{
{
// Create an IPv4 TCP/IP socket.
TcpListener listener = new TcpListener(IPAddress.Any, 11000);
// Listen for incoming connections.
listener.Start();
while (true)
while (true)
{
TcpClient clientRequest = null;
TcpClient clientRequest;
// Application blocks while waiting for an incoming connection.
// Type CNTL-C to terminate the server.
clientRequest = listener.AcceptTcpClient();
Console.WriteLine("Client connected.");
// A client has connected.
try
{
AuthenticateClient (clientRequest);
AuthenticateClient(clientRequest);
}
catch (Exception e)
{
Console.WriteLine(e);
continue;
}
}
}
//<snippet1>

//<snippet1>
public static void AuthenticateClient(TcpClient clientRequest)
{
NetworkStream stream = clientRequest.GetStream();
NetworkStream stream = clientRequest.GetStream();
// Create the NegotiateStream.
NegotiateStream authStream = new NegotiateStream(stream, false);
NegotiateStream authStream = new NegotiateStream(stream, false);
// Save the current client and NegotiateStream instance
// in a ClientState object.
ClientState cState = new ClientState(authStream, clientRequest);
// Listen for the client authentication request.
authStream.BeginAuthenticateAsServer (
new AsyncCallback(EndAuthenticateCallback),
cState
);
// Wait until the authentication completes.
cState.Waiter.WaitOne();
cState.Waiter.Reset();
authStream.BeginRead(cState.Buffer, 0, cState.Buffer.Length,
new AsyncCallback(EndReadCallback),
cState);
cState.Waiter.WaitOne();
// Finished with the current client.
authStream.Close();
clientRequest.Close();
}
//</snippet1>
// The following method is invoked by the
// BeginAuthenticateAsServer callback delegate.
Task authTask = authStream
.AuthenticateAsServerAsync()
.ContinueWith(task => { EndAuthenticateCallback(cState); });

//<snippet2>
public static void EndAuthenticateCallback (IAsyncResult ar)
{
// Get the saved data.
ClientState cState = (ClientState) ar.AsyncState;
TcpClient clientRequest = cState.Client;
NegotiateStream authStream = (NegotiateStream) cState.AuthenticatedStream;
Console.WriteLine("Ending authentication.");
// Any exceptions that occurred during authentication are
// thrown by the EndAuthenticateAsServer method.
try
try
{
// This call blocks until the authentication is complete.
authStream.EndAuthenticateAsServer(ar);
authTask.Wait();
}
catch (AuthenticationException e)
{
Console.WriteLine(e);
Console.WriteLine("Authentication failed - closing connection.");
cState.Waiter.Set();
return;
}
catch (Exception e)
{
Console.WriteLine(e);
Console.WriteLine("Closing connection.");
cState.Waiter.Set();
return;
}

Task<int> readTask = authStream
.ReadAsync(cState.Buffer, 0, cState.Buffer.Length);

readTask
.ContinueWith((task) => { EndReadCallback(cState, task.Result); })
.Wait();
// Finished with the current client.
authStream.Close();
clientRequest.Close();
}
//</snippet1>

//<snippet2>
private static void EndAuthenticateCallback(ClientState cState)
{
// Get the saved data.
NegotiateStream authStream = (NegotiateStream)cState.AuthenticatedStream;
Console.WriteLine("Ending authentication.");

// Display properties of the authenticated client.
IIdentity id = authStream.RemoteIdentity;
Console.WriteLine("{0} was authenticated using {1}.",
id.Name,
Console.WriteLine("{0} was authenticated using {1}.",
id.Name,
id.AuthenticationType
);
cState.Waiter.Set();
}
);
}
//</snippet2>

//<snippet3>
public static void EndReadCallback(IAsyncResult ar)
private static void EndReadCallback(ClientState cState, int bytes)
{
// Get the saved data.
ClientState cState = (ClientState) ar.AsyncState;
TcpClient clientRequest = cState.Client;
NegotiateStream authStream = (NegotiateStream) cState.AuthenticatedStream;
// Get the buffer that stores the message sent by the client.
int bytes = -1;
NegotiateStream authStream = (NegotiateStream)cState.AuthenticatedStream;
// Read the client message.
try
{
bytes = authStream.EndRead(ar);
cState.Message.Append(Encoding.UTF8.GetChars(cState.Buffer, 0, bytes));
if (bytes != 0)
{
authStream.BeginRead(cState.Buffer, 0, cState.Buffer.Length,
new AsyncCallback(EndReadCallback),
cState);
return;
}
cState.Message.Append(Encoding.UTF8.GetChars(cState.Buffer, 0, bytes));
if (bytes != 0)
{
Task<int> readTask = authStream.ReadAsync(cState.Buffer, 0, cState.Buffer.Length);
readTask
.ContinueWith(task => { EndReadCallback(cState, task.Result); })
.Wait();

return;
}
}
catch (Exception e)
{
// A real application should do something
// useful here, such as logging the failure.
Console.WriteLine("Client message exception:");
Console.WriteLine(e);
cState.Waiter.Set();
return;
}
IIdentity id = authStream.RemoteIdentity;
Console.WriteLine("{0} says {1}", id.Name, cState.Message.ToString());
cState.Waiter.Set();
}
//</snippet3>
//</snippet3>
}
// ClientState is the AsyncState object.
internal class ClientState
{
private AuthenticatedStream authStream = null;
private TcpClient client = null;
byte[] buffer = new byte[2048];
StringBuilder message = null;
ManualResetEvent waiter = new ManualResetEvent(false);
private StringBuilder _message = null;

internal ClientState(AuthenticatedStream a, TcpClient theClient)
{
authStream = a;
client = theClient;
}
internal TcpClient Client
{
get { return client;}
}
internal AuthenticatedStream AuthenticatedStream
{
get { return authStream;}
}
internal byte[] Buffer
{
get { return buffer;}
AuthenticatedStream = a;
Client = theClient;
}
internal TcpClient Client { get; }

internal AuthenticatedStream AuthenticatedStream { get; }

internal byte[] Buffer { get; } = new byte[2048];

internal StringBuilder Message
{
get
{
if (message == null)
message = new StringBuilder();
return message;
}
}
internal ManualResetEvent Waiter
{
get
{
return waiter;
}
get { return _message ??= new StringBuilder(); }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,75 +28,60 @@ public static void Main(String[] args)
Console.WriteLine("Client connected to {0}.", remoteEP.ToString());
// Ensure the client does not close when there is
// still data to be sent to the server.
client.LingerState = (new LingerOption(true, 0));
client.LingerState = new LingerOption(true, 0);
//<snippet3>
// Request authentication.
NetworkStream clientStream = client.GetStream();
NegotiateStream authStream = new NegotiateStream(clientStream, false);
NegotiateStream authStream = new NegotiateStream(clientStream, false);
//</snippet1>
// Pass the NegotiateStream as the AsyncState object
// so that it is available to the callback delegate.
IAsyncResult ar = authStream.BeginAuthenticateAsClient(
new AsyncCallback(EndAuthenticateCallback),
authStream
);
Task authenticateTask = authStream
.AuthenticateAsClientAsync()
.ContinueWith(task =>
{
Console.WriteLine("Client ending authentication...");
Console.WriteLine("ImpersonationLevel: {0}", authStream.ImpersonationLevel);
});

//</snippet2>
Console.WriteLine("Client waiting for authentication...");
// Wait until the result is available.
ar.AsyncWaitHandle.WaitOne();
authenticateTask.Wait();
// Display the properties of the authenticated stream.
AuthenticatedStreamReporter.DisplayProperties(authStream);
// Send a message to the server.
// Encode the test data into a byte array.
byte[] message = Encoding.UTF8.GetBytes("Hello from the client.");
ar = authStream.BeginWrite(message, 0, message.Length,
new AsyncCallback(EndWriteCallback),
authStream);
Task writeTask = authStream
.WriteAsync(message, 0, message.Length)
.ContinueWith(task =>
{
Console.WriteLine("Client ending write operation...");
});

//</snippet3>
ar.AsyncWaitHandle.WaitOne();
writeTask.Wait();
Console.WriteLine("Sent {0} bytes.", message.Length);
// Close the client connection.
authStream.Close();
Console.WriteLine("Client closed.");
}
//<snippet5>
// The following method is called when the authentication completes.
public static void EndAuthenticateCallback (IAsyncResult ar)
{
Console.WriteLine("Client ending authentication...");
NegotiateStream authStream = (NegotiateStream) ar.AsyncState;
Console.WriteLine("ImpersonationLevel: {0}", authStream.ImpersonationLevel);

// End the asynchronous operation.
authStream.EndAuthenticateAsClient(ar);
}
//</snippet5>
//<snippet4>
// The following method is called when the write operation completes.
public static void EndWriteCallback (IAsyncResult ar)
{
Console.WriteLine("Client ending write operation...");
NegotiateStream authStream = (NegotiateStream) ar.AsyncState;

// End the asynchronous operation.
authStream.EndWrite(ar);
}
//</snippet4>
}
//<snippet6>

//<snippet6>
// The following class displays the properties of an authenticatedStream.
public class AuthenticatedStreamReporter
{
public static void DisplayProperties(AuthenticatedStream stream)
{
Console.WriteLine("IsAuthenticated: {0}", stream.IsAuthenticated);
Console.WriteLine("IsMutuallyAuthenticated: {0}", stream.IsMutuallyAuthenticated);
Console.WriteLine("IsEncrypted: {0}", stream.IsEncrypted);
Console.WriteLine("IsSigned: {0}", stream.IsSigned);
Console.WriteLine("IsServer: {0}", stream.IsServer);
{
public static void DisplayProperties(AuthenticatedStream stream)
{
Console.WriteLine("IsAuthenticated: {0}", stream.IsAuthenticated);
Console.WriteLine("IsMutuallyAuthenticated: {0}", stream.IsMutuallyAuthenticated);
Console.WriteLine("IsEncrypted: {0}", stream.IsEncrypted);
Console.WriteLine("IsSigned: {0}", stream.IsSigned);
Console.WriteLine("IsServer: {0}", stream.IsServer);
}
}
}
//</snippet6>
//</snippet6>
}
//</snippet0>