Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
447ca89baa | ||
|
|
8cc4279e1d | ||
|
|
4ede16318a | ||
|
|
1374758ffb | ||
|
|
4bfe7d8bc9 | ||
|
|
8c19366314 | ||
|
|
3a134be9df | ||
|
|
50efc7702b |
@@ -183,6 +183,12 @@ namespace NSspi.Contexts
|
||||
{
|
||||
throw new ObjectDisposedException( "ServerContext" );
|
||||
}
|
||||
else if( this.Initialized == false )
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The server context has not been completely initialized."
|
||||
);
|
||||
}
|
||||
else if( impersonating )
|
||||
{
|
||||
throw new InvalidOperationException( "Cannot impersonate again while already impersonating." );
|
||||
|
||||
60
NSspi/EnumMgr.cs
Normal file
60
NSspi/EnumMgr.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace NSspi
|
||||
{
|
||||
[AttributeUsage( AttributeTargets.Field )]
|
||||
public class EnumStringAttribute : Attribute
|
||||
{
|
||||
public EnumStringAttribute( string text )
|
||||
{
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
public string Text { get; private set; }
|
||||
}
|
||||
|
||||
public class EnumMgr
|
||||
{
|
||||
public static string ToText( Enum value )
|
||||
{
|
||||
FieldInfo field = value.GetType().GetField( value.ToString() );
|
||||
|
||||
EnumStringAttribute[] attribs = (EnumStringAttribute[])field.GetCustomAttributes( typeof( EnumStringAttribute ), false );
|
||||
|
||||
if( attribs == null || attribs.Length == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return attribs[0].Text;
|
||||
}
|
||||
}
|
||||
|
||||
public static T FromText<T>( string text )
|
||||
{
|
||||
FieldInfo[] fields = typeof( T ).GetFields();
|
||||
|
||||
EnumStringAttribute[] attribs;
|
||||
|
||||
foreach( FieldInfo field in fields )
|
||||
{
|
||||
attribs = (EnumStringAttribute[])field.GetCustomAttributes( typeof( EnumStringAttribute ), false );
|
||||
|
||||
foreach( EnumStringAttribute attrib in attribs )
|
||||
{
|
||||
if( attrib.Text == text )
|
||||
{
|
||||
return (T)field.GetValue( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException( "Could not find a matching enumeration value for the text '" + text + "'." );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,7 @@
|
||||
<Compile Include="Contexts\ImpersonationHandle.cs" />
|
||||
<Compile Include="Contexts\SafeContextHandle.cs" />
|
||||
<Compile Include="Credentials\CurrentCredential.cs" />
|
||||
<Compile Include="EnumMgr.cs" />
|
||||
<Compile Include="SecPkgInfo.cs" />
|
||||
<Compile Include="Contexts\ServerContext.cs" />
|
||||
<Compile Include="Credentials\ClientCredential.cs" />
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyVersion("0.1.1.0")]
|
||||
[assembly: AssemblyFileVersion("0.1.1.0")]
|
||||
|
||||
@@ -70,7 +70,12 @@ namespace NSspi
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format( "{0}. Error Code = '{1:X}'.", this.message, this.errorCode );
|
||||
return string.Format(
|
||||
"{0}. Error Code = '0x{1:X}' - \"{2}\".",
|
||||
this.message,
|
||||
this.errorCode,
|
||||
EnumMgr.ToText(this.errorCode)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,56 +30,100 @@ namespace NSspi
|
||||
/// <summary>
|
||||
/// The request completed successfully
|
||||
/// </summary>
|
||||
[EnumString( "No error" )]
|
||||
OK = 0x00000000,
|
||||
|
||||
/// <summary>
|
||||
/// The token returned by the context needs to be provided to the cooperating party
|
||||
/// to continue construction of the context.
|
||||
/// </summary>
|
||||
ContinueNeeded = 0x00090312,
|
||||
[EnumString( "Authentication cycle needs to continue" )]
|
||||
ContinueNeeded = 0x00090312,
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a client calls InitializeSecurityContext to indicate that the client
|
||||
/// must call CompleteAuthToken.
|
||||
/// </summary>
|
||||
[EnumString( "Authentication cycle needs to perform a 'complete'." )]
|
||||
CompleteNeeded = 0x00090313,
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a client calls InitializeSecurityContext to indicate that the client
|
||||
/// must call CompleteAuthToken and pass the result to the server.
|
||||
/// </summary>
|
||||
CompAndContinue = 0x00090314,
|
||||
[EnumString( "Authentication cycle needs to perform a 'complete' and then continue." )]
|
||||
CompAndContinue = 0x00090314,
|
||||
|
||||
/// <summary>
|
||||
/// An attempt to use the context was performed after the context's expiration time elapsed.
|
||||
/// </summary>
|
||||
ContextExpired = 0x00090317,
|
||||
CredentialsNeeded = 0x00090320,
|
||||
[EnumString( "The security context was used after its expiration time passed." )]
|
||||
ContextExpired = 0x00090317,
|
||||
|
||||
[EnumString( "The credentials supplied to the security context were not fully initialized." )]
|
||||
CredentialsNeeded = 0x00090320,
|
||||
|
||||
[EnumString( "The context data must be re-negotiated with the peer" )]
|
||||
Renegotiate = 0x00090321,
|
||||
|
||||
// Errors
|
||||
[EnumString( "Not enough memory.")]
|
||||
OutOfMemory = 0x80090300,
|
||||
|
||||
[EnumString( "The handle provided to the API was invalid.")]
|
||||
InvalidHandle = 0x80090301,
|
||||
|
||||
[EnumString( "The attempted operation is not supported")]
|
||||
Unsupported = 0x80090302,
|
||||
|
||||
[EnumString( "The specified principle is not known in the authentication system.")]
|
||||
TargetUnknown = 0x80090303,
|
||||
|
||||
[EnumString( "An internal error occurred" )]
|
||||
InternalError = 0x80090304,
|
||||
|
||||
/// <summary>
|
||||
/// No security provider package was found with the given name.
|
||||
/// </summary>
|
||||
[EnumString( "The requested security package was not found.")]
|
||||
PackageNotFound = 0x80090305,
|
||||
|
||||
NotOwner = 0x80090306,
|
||||
CannotInstall = 0x80090307,
|
||||
|
||||
/// <summary>
|
||||
/// A token was provided that contained incorrect or corrupted data.
|
||||
/// </summary>
|
||||
[EnumString("The provided authentication token is invalid or corrupted.")]
|
||||
InvalidToken = 0x80090308,
|
||||
|
||||
CannotPack = 0x80090309,
|
||||
QopNotSupported = 0x8009030A,
|
||||
|
||||
/// <summary>
|
||||
/// Impersonation is not supported.
|
||||
/// </summary>
|
||||
[EnumString("Impersonation is not supported with the current security package.")]
|
||||
NoImpersonation = 0x8009030B,
|
||||
|
||||
[EnumString("The logon was denied, perhaps because the provided credentials were incorrect.")]
|
||||
LogonDenied = 0x8009030C,
|
||||
|
||||
|
||||
[EnumString( "The credentials provided are not recognized by the selected security package.")]
|
||||
UnknownCredentials = 0x8009030D,
|
||||
|
||||
[EnumString( "No credentials are available in the selected security package.")]
|
||||
NoCredentials = 0x8009030E,
|
||||
|
||||
[EnumString( "A message that was provided to the Decrypt or VerifySignature functions was altered " +
|
||||
"after it was created.")]
|
||||
MessageAltered = 0x8009030F,
|
||||
|
||||
[EnumString( "A message was received out of the expected order.")]
|
||||
OutOfSequence = 0x80090310,
|
||||
|
||||
[EnumString( "The current security package cannot contact an authenticating authority.")]
|
||||
NoAuthenticatingAuthority = 0x80090311,
|
||||
|
||||
/// <summary>
|
||||
|
||||
40
Readme.txt
40
Readme.txt
@@ -1,40 +1,22 @@
|
||||
This projects provides a C# / .Net interface to the Windows Integrated Authentication API,
|
||||
better known as SSPI (Security Service Provider Interface).
|
||||
This projects provides a C# / .Net interface to the Windows Integrated Authentication API, better known as SSPI (Security Service Provider Interface). This allows a custom client / server system to authenticate users using their existing logon credentials. This allows a developer to provide Single-Sign-On in their application.
|
||||
|
||||
The project is provided as a .Net 4.0 assembly, but can just as easily be upgraded to .Net 4.5
|
||||
or later. The solution file can be opened by Visual Studio 2010 SP1, Visual Studio 2012, or
|
||||
later Visual Studio editions.
|
||||
The API provides raw access to authentication tokens so that authentication can be easily integrated into any networking system - you can send the tokens over a socket, a remoting interface, or heck even a serial port if you want; they're just bytes. Clients and servers may exchange encrypted and signed messages, and the server can perform client impersonation.
|
||||
|
||||
The SSPI API provides an interface for real authentication protocols, such as Kerberos or
|
||||
NTLM, to be invoked transparently by client and server code in order to perform authentication
|
||||
and message manipulation. These authentication protocols are better known as 'security packages'.
|
||||
The project is provided as a .Net 4.0 assembly, but can just as easily be upgraded to .Net 4.5 or later. The solution file can be opened by Visual Studio 2010 SP1, Visual Studio 2012, or later Visual Studio editions.
|
||||
|
||||
The SSPI API exposes these packages using a common API, and so a program may invoke one or the
|
||||
other with only minor changes in implementation. SSPI also supports the 'negotiate' 'meta'
|
||||
package, that allows a client and server to decide dynamically which real security provider to
|
||||
use, and then itself provides a passthrough interface to the real package.
|
||||
The SSPI API provides an interface for real authentication protocols, such as Kerberos or NTLM, to be invoked transparently by client and server code in order to perform authentication and message manipulation. These authentication protocols are better known as 'security packages'.
|
||||
|
||||
The SSPI API exposes these packages using a common API, and so a program may invoke one or the other with only minor changes in implementation. SSPI also supports the 'negotiate' 'meta' package, that allows a client and server to decide dynamically which real security provider to use, and then itself provides a passthrough interface to the real package.
|
||||
|
||||
==== Usage ====
|
||||
|
||||
Typically, a client acquires some form of a credential, either from the currently logged on
|
||||
user's security context, by acquiring a username and password from the user, or by some other
|
||||
means. The server acquires a credential in a similar manner. Each uses their credentials to
|
||||
identify themselves to each other.
|
||||
Typically, a client acquires some form of a credential, either from the currently logged on user's security context, by acquiring a username and password from the user, or by some other means. The server acquires a credential in a similar manner. Each uses their credentials to identify themselves to each other.
|
||||
|
||||
A client and a server each start with uninitialized security contexts. They exchange negotiation
|
||||
and authentication tokens to perform authentication, and if all succeeds, they create a shared
|
||||
security context in the form of a client's context and a server's context. The effectively shared
|
||||
context agrees on the security package to use (kerberos, NTLM), and what parameters to use
|
||||
for message passing. Every new client that authenticates with a server creates a new security
|
||||
context specific to that client-server pairing.
|
||||
A client and a server each start with uninitialized security contexts. They exchange negotiation and authentication tokens to perform authentication, and if all succeeds, they create a shared security context in the form of a client's context and a server's context. The effectively shared context agrees on the security package to use (kerberos, NTLM), and what parameters to use for message passing. Every new client that authenticates with a server creates a new security context specific to that client-server pairing.
|
||||
|
||||
From the software perspective, a client security context initializes itself by exchanging
|
||||
authentication tokens with a server; the server initializes itself by exchanging authentication
|
||||
tokens with the client.
|
||||
From the software perspective, a client security context initializes itself by exchanging authentication tokens with a server; the server initializes itself by exchanging authentication tokens with the client.
|
||||
|
||||
This API provides raw access to the authentication tokens created during the negotiation and
|
||||
authentication process. In this manner, any application can integrate SSPI-based authentication
|
||||
by deciding for themselves how to integrate the tokens into their application protocol.
|
||||
This API provides raw access to the authentication tokens created during the negotiation and authentication process. In this manner, any application can integrate SSPI-based authentication by deciding for themselves how to integrate the tokens into their application protocol.
|
||||
|
||||
The project is broken up into 3 chunks:
|
||||
|
||||
@@ -66,5 +48,3 @@ Relevant StackOverflow questions:
|
||||
|
||||
"AcquireCredentialsHandle returns massive expiration time"
|
||||
- http://stackoverflow.com/questions/24478056/
|
||||
|
||||
|
||||
|
||||
8
TestClient/ClientForm.Designer.cs
generated
8
TestClient/ClientForm.Designer.cs
generated
@@ -148,6 +148,7 @@
|
||||
this.sendTextbox.Location = new System.Drawing.Point(6, 19);
|
||||
this.sendTextbox.Multiline = true;
|
||||
this.sendTextbox.Name = "sendTextbox";
|
||||
this.sendTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.sendTextbox.Size = new System.Drawing.Size(302, 298);
|
||||
this.sendTextbox.TabIndex = 7;
|
||||
//
|
||||
@@ -190,6 +191,7 @@
|
||||
this.receiveTextbox.Location = new System.Drawing.Point(3, 16);
|
||||
this.receiveTextbox.Multiline = true;
|
||||
this.receiveTextbox.Name = "receiveTextbox";
|
||||
this.receiveTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.receiveTextbox.Size = new System.Drawing.Size(308, 338);
|
||||
this.receiveTextbox.TabIndex = 10;
|
||||
//
|
||||
@@ -231,7 +233,7 @@
|
||||
this.disconnectButton.Text = "Disconnect";
|
||||
this.disconnectButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Form1
|
||||
// ClientForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
@@ -244,8 +246,8 @@
|
||||
this.Controls.Add(this.serverTextBox);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Name = "Form1";
|
||||
this.Text = "Form1";
|
||||
this.Name = "ClientForm";
|
||||
this.Text = "Client - SSPI Demo";
|
||||
((System.ComponentModel.ISupportInitialize)(this.portNumeric)).EndInit();
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
|
||||
@@ -112,7 +112,10 @@ namespace TestProtocol
|
||||
byte[] readBuffer = new byte[65536];
|
||||
|
||||
ProtocolOp operation;
|
||||
int length;
|
||||
int messageLength;
|
||||
int remaining;
|
||||
int chunkLength;
|
||||
int position;
|
||||
|
||||
while( this.running )
|
||||
{
|
||||
@@ -132,10 +135,24 @@ namespace TestProtocol
|
||||
|
||||
// Read the length
|
||||
this.socket.Receive( readBuffer, 4, SocketFlags.None );
|
||||
length = ByteWriter.ReadInt32_BE( readBuffer, 0 );
|
||||
messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 );
|
||||
|
||||
if( readBuffer.Length < messageLength )
|
||||
{
|
||||
readBuffer = new byte[messageLength];
|
||||
}
|
||||
|
||||
// Read the data
|
||||
this.socket.Receive( readBuffer, length, SocketFlags.None );
|
||||
// Keep in mind that Socket.Receive may return less data than asked for.
|
||||
remaining = messageLength;
|
||||
chunkLength = 0;
|
||||
position = 0;
|
||||
while( remaining > 0 )
|
||||
{
|
||||
chunkLength = this.socket.Receive( readBuffer, position, remaining, SocketFlags.None );
|
||||
remaining -= chunkLength;
|
||||
position += chunkLength;
|
||||
}
|
||||
|
||||
}
|
||||
catch( SocketException e )
|
||||
@@ -143,7 +160,8 @@ namespace TestProtocol
|
||||
if( e.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
e.SocketErrorCode == SocketError.Interrupted ||
|
||||
e.SocketErrorCode == SocketError.OperationAborted ||
|
||||
e.SocketErrorCode == SocketError.Shutdown )
|
||||
e.SocketErrorCode == SocketError.Shutdown ||
|
||||
e.SocketErrorCode == SocketError.ConnectionReset )
|
||||
{
|
||||
// Shutting down.
|
||||
break;
|
||||
@@ -158,8 +176,8 @@ namespace TestProtocol
|
||||
|
||||
if( this.Received != null )
|
||||
{
|
||||
byte[] dataCopy = new byte[length];
|
||||
Array.Copy( readBuffer, 0, dataCopy, 0, length );
|
||||
byte[] dataCopy = new byte[messageLength];
|
||||
Array.Copy( readBuffer, 0, dataCopy, 0, messageLength );
|
||||
Message message = new Message( operation, dataCopy );
|
||||
|
||||
try
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using NSspi;
|
||||
|
||||
namespace TestProtocol
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using NSspi;
|
||||
|
||||
public class CustomServer
|
||||
{
|
||||
private Thread receiveThread;
|
||||
@@ -149,7 +139,9 @@ namespace TestProtocol
|
||||
byte[] readBuffer = new byte[65536];
|
||||
|
||||
ProtocolOp operation;
|
||||
int length;
|
||||
int messageLength;
|
||||
int position;
|
||||
int remaining;
|
||||
|
||||
while( this.running )
|
||||
{
|
||||
@@ -157,7 +149,7 @@ namespace TestProtocol
|
||||
{
|
||||
// |--4 bytes--|--4 bytes--|---N--|
|
||||
// Every command is a TLV - | Operation | Length | Data |
|
||||
|
||||
int chunkLength;
|
||||
|
||||
// Read the operation.
|
||||
this.readSocket.Receive( readBuffer, 4, SocketFlags.None );
|
||||
@@ -169,11 +161,24 @@ namespace TestProtocol
|
||||
|
||||
// Read the length
|
||||
this.readSocket.Receive( readBuffer, 4, SocketFlags.None );
|
||||
length = ByteWriter.ReadInt32_BE( readBuffer, 0 );
|
||||
messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 );
|
||||
|
||||
if( readBuffer.Length < messageLength )
|
||||
{
|
||||
readBuffer = new byte[messageLength];
|
||||
}
|
||||
|
||||
// Read the data
|
||||
this.readSocket.Receive( readBuffer, length, SocketFlags.None );
|
||||
|
||||
// Keep in mind that Socket.Receive may return less data than asked for.
|
||||
remaining = messageLength;
|
||||
chunkLength = 0;
|
||||
position = 0;
|
||||
while( remaining > 0 )
|
||||
{
|
||||
chunkLength = this.readSocket.Receive( readBuffer, position, remaining, SocketFlags.None );
|
||||
remaining -= chunkLength;
|
||||
position += chunkLength;
|
||||
}
|
||||
}
|
||||
catch( SocketException e )
|
||||
{
|
||||
@@ -196,8 +201,8 @@ namespace TestProtocol
|
||||
|
||||
if( this.Received != null )
|
||||
{
|
||||
byte[] dataCopy = new byte[length];
|
||||
Array.Copy( readBuffer, 0, dataCopy, 0, length );
|
||||
byte[] dataCopy = new byte[messageLength];
|
||||
Array.Copy( readBuffer, 0, dataCopy, 0, messageLength );
|
||||
Message message = new Message( operation, dataCopy );
|
||||
|
||||
try
|
||||
|
||||
22
TestServer/ServerForm.Designer.cs
generated
22
TestServer/ServerForm.Designer.cs
generated
@@ -38,13 +38,13 @@
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.serverUsernameTextbox = new System.Windows.Forms.TextBox();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.impersonateButton = new System.Windows.Forms.Button();
|
||||
this.signButton = new System.Windows.Forms.Button();
|
||||
this.encryptButton = new System.Windows.Forms.Button();
|
||||
this.sendTextbox = new System.Windows.Forms.TextBox();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this.receivedTextbox = new System.Windows.Forms.TextBox();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.impersonateButton = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)(this.portNumeric)).BeginInit();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.groupBox2.SuspendLayout();
|
||||
@@ -168,6 +168,15 @@
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "Send a message to the client";
|
||||
//
|
||||
// impersonateButton
|
||||
//
|
||||
this.impersonateButton.Location = new System.Drawing.Point(262, 350);
|
||||
this.impersonateButton.Name = "impersonateButton";
|
||||
this.impersonateButton.Size = new System.Drawing.Size(116, 23);
|
||||
this.impersonateButton.TabIndex = 4;
|
||||
this.impersonateButton.Text = "Test impersonation";
|
||||
this.impersonateButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// signButton
|
||||
//
|
||||
this.signButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
@@ -196,6 +205,7 @@
|
||||
this.sendTextbox.Location = new System.Drawing.Point(6, 19);
|
||||
this.sendTextbox.Multiline = true;
|
||||
this.sendTextbox.Name = "sendTextbox";
|
||||
this.sendTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.sendTextbox.Size = new System.Drawing.Size(400, 323);
|
||||
this.sendTextbox.TabIndex = 0;
|
||||
//
|
||||
@@ -216,6 +226,7 @@
|
||||
this.receivedTextbox.Location = new System.Drawing.Point(3, 16);
|
||||
this.receivedTextbox.Multiline = true;
|
||||
this.receivedTextbox.Name = "receivedTextbox";
|
||||
this.receivedTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.receivedTextbox.Size = new System.Drawing.Size(407, 370);
|
||||
this.receivedTextbox.TabIndex = 0;
|
||||
//
|
||||
@@ -236,15 +247,6 @@
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(838, 395);
|
||||
this.tableLayoutPanel1.TabIndex = 7;
|
||||
//
|
||||
// impersonateButton
|
||||
//
|
||||
this.impersonateButton.Location = new System.Drawing.Point(262, 350);
|
||||
this.impersonateButton.Name = "impersonateButton";
|
||||
this.impersonateButton.Size = new System.Drawing.Size(116, 23);
|
||||
this.impersonateButton.TabIndex = 4;
|
||||
this.impersonateButton.Text = "Test impersonation";
|
||||
this.impersonateButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ServerForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace TestServer
|
||||
|
||||
private void server_Disconnected()
|
||||
{
|
||||
this.running = false;
|
||||
this.running = true;
|
||||
this.initializing = true;
|
||||
this.connected = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user