Tom

I don't know what '.NET TELNET tool' you refer to - but I wrote a customized
terminal emulator in .NET for a client that makes a connection Telnet/SSL
connection to UniVerse on Solaris and tested fine on Linux. 

For the server configuration and details on setting up the secure telnet
daemon take a look at my blog (you can get to it from my website).

As far as the client is concerned, I can't give too much away as I don't own
it - it was funded by a customer - but I can probably share the following if
you're tempted to roll your own!

1. for the low level connection I use a SecureTCPClient that wraps an
SslStream - here are the salient pieces:

public delegate void SecureConnectionResultsCallback(object sender,
SecureConnectionResults args);
    public delegate void ReceivedDataEvent( object sender, string data);
    public delegate void ReceiveErrorEvent( object sender );
    
    public class SecureTcpState
    {
           public SslStream stream;
            public System.Byte[] Buffer;

            public SecureTcpState()
            {
                this.Buffer = new System.Byte[8192];
            }
    }

    public class SecureConnectionResults
    {
        private SslStream secureStream;
        private Exception asyncException;
       
        internal SecureConnectionResults(SslStream sslStream)
        {
            this.secureStream = sslStream;
        }

        internal SecureConnectionResults(Exception exception)
        {
            this.asyncException = exception;
        }

        public Exception AsyncException { get { return asyncException; } }
        public SslStream SecureStream { get { return secureStream; } }
    }

    /// <summary>
    /// SecureTcpClient: a TCP client socket with ssl streaming
    /// </summary>
    public class SecureTcpClient: IDisposable
    {
        X509CertificateCollection clientCertificates = null;
        RemoteCertificateValidationCallback certValidationCallback= null;
        SecureConnectionResultsCallback connectionCallback = null;
        bool checkCertificateRevocation = true; 
        SslStream sslStream = null;
        TcpClient client = null;        
        IPEndPoint remoteEndPoint = null;
        string remoteHostName = string.Empty;
        SslProtocols protocols = SslProtocols.None;
        protected int disposed = 0;        
        protected Semaphore _sendLock = null; // guard against possible
BeginSend clashes
        protected uint _keepAlive = 0;
        protected System.Text.Encoding _encoding =
System.Text.Encoding.GetEncoding(1252);

        // async callbacks for connection and authentication
        AsyncCallback onConnected;
        AsyncCallback onAuthenticateAsClient;

        // async callback for receiving
        private AsyncCallback callbackProc = null;
        private AsyncCallback callbackEndDispatch = null;

        public event ReceivedDataEvent onReceived = null;
        public event ReceiveErrorEvent onReceiveError = null;

        public SecureTcpClient(SecureConnectionResultsCallback callback)
            : this(callback,null,SslProtocols.Default){
        }
        public SecureTcpClient(SecureConnectionResultsCallback callback, 
            RemoteCertificateValidationCallback certValidationCallback)
            : this(callback, certValidationCallback, SslProtocols.Default){
        }
 
        public SecureTcpClient(SecureConnectionResultsCallback callback, 
            RemoteCertificateValidationCallback certValidationCallback, 
            SslProtocols sslProtocols){
            if (callback == null) {
                throw new ArgumentNullException("callback");
            }

            onConnected = new AsyncCallback(OnConnected);
            onAuthenticateAsClient = new
AsyncCallback(OnAuthenticateAsClient);
            this.certValidationCallback = certValidationCallback;
            this.connectionCallback = callback;
            protocols = sslProtocols;
            this.disposed = 0;
        }
 
        ~SecureTcpClient(){
            Dispose();
        }
 
        public bool CheckCertificateRevocation{
            get { return checkCertificateRevocation; }
            set {checkCertificateRevocation = value;}
        }
 
        public void StartConnecting(string remoteHostName, IPEndPoint
remoteEndPoint){
            StartConnecting(remoteHostName,remoteEndPoint,null);
        }
 

        public void StartConnecting(string remoteHostName, IPEndPoint
remoteEndPoint, 
            X509CertificateCollection clientCertificates){
            if (string.IsNullOrEmpty(remoteHostName)) {
                throw new ArgumentException("Value cannot be null or empty",
"remoteHostName");
            }

            if (remoteEndPoint == null) {
                throw new ArgumentNullException("remoteEndPoint");
            }
         
            this.clientCertificates = clientCertificates;
            this.remoteHostName = remoteHostName;
            this.remoteEndPoint = remoteEndPoint;

            if (client != null) {
                client.Close();
            }
        
            client = new TcpClient(remoteEndPoint.AddressFamily);
            
            client.BeginConnect(remoteEndPoint.Address,
                remoteEndPoint.Port,
                this.onConnected,null);        
        }
 
        public void Close(){
            Dispose();
        }
 
        void OnConnected(IAsyncResult result){
            try{
                bool leaveStreamOpen = false;//close the socket when done

                if (this.certValidationCallback != null) {
                    sslStream = new SslStream(client.GetStream(),
leaveStreamOpen, this.certValidationCallback);
                } else {
                    sslStream = new SslStream(client.GetStream(),
leaveStreamOpen);
                }

                sslStream.AuthenticateAsClient(remoteHostName, null,
SslProtocols.Ssl3, false);
                this.connectionCallback(this, new
SecureConnectionResults(sslStream));

                // start async reading
                SecureTcpState state = new SecureTcpState();
                state.stream = sslStream;
                callbackProc = new
System.AsyncCallback(receivedDataCallback);
                sslStream.BeginRead(state.Buffer, 0, state.Buffer.Length,
callbackProc, state);

            }
            catch (Exception ex){
                if (sslStream != null){
                    sslStream.Dispose();
                    sslStream = null;
                }
                this.connectionCallback(this,new
SecureConnectionResults(ex));
            }
        }
 
        void OnAuthenticateAsClient(IAsyncResult result){           
            try{
                sslStream = result.AsyncState as SslStream;
                sslStream.EndAuthenticateAsClient(result);
                this.connectionCallback(this,new SecureConnectionResults(
sslStream));

                SecureTcpState state = new SecureTcpState();
                state.stream = sslStream;
                callbackProc = new
System.AsyncCallback(receivedDataCallback);
                sslStream.BeginRead(state.Buffer, 0, state.Buffer.Length,
callbackProc, state);
            }catch (Exception ex){
                if (sslStream != null){
                    sslStream.Dispose();
                    sslStream = null;
                }               
                this.connectionCallback(this, new
SecureConnectionResults(ex));
            }
        }

        public void receivedDataCallback(System.IAsyncResult ar)
        {
            // Get The connection socket from the callback
            SecureTcpState StateObject = (SecureTcpState)ar.AsyncState;

            // Get The data , if any
            int nBytesRec = 0;

            try {
                nBytesRec = StateObject.stream.EndRead(ar);
            }
            catch {
                nBytesRec = 0;
            }

            if (nBytesRec > 0) {
                
                string sReceived = "";

                sReceived = _encoding.GetString(StateObject.Buffer, 0,
nBytesRec);

                if (onReceived != null) {
                    onReceived(this, sReceived);
                }
                
                try {
                    StateObject.stream.BeginRead(StateObject.Buffer, 0,
StateObject.Buffer.Length,
                         new System.AsyncCallback(receivedDataCallback),
StateObject);
                } catch { // disconnected
                    if (onReceiveError != null) {
                        onReceiveError(this);
                    }                    
                }
            } else {
                // If no data was recieved then the connection is probably
dead
                //System.Console.WriteLine("Disconnected");
                if (onReceiveError != null) {
                    onReceiveError(this);
                }     
                StateObject.stream.Close();               
            }
        }

        public bool send(String s){            
            if (_sendLock == null) {
                _sendLock = new Semaphore(0, 1); 
            } else {
                _sendLock.WaitOne();
            }

            try {
                System.Byte[] smk = new System.Byte[s.Length];

                smk = _encoding.GetBytes(s);

                if (callbackEndDispatch == null) {
                    callbackEndDispatch = new
System.AsyncCallback(EndDispatchMessage);
                }
                SecureTcpState state = new SecureTcpState();
                state.stream = sslStream;

                System.IAsyncResult ar =
sslStream.BeginWrite(smk,0,smk.Length,callbackEndDispatch,state);
            }
            catch (System.Exception CurException) {
                _sendLock.Release();
                return false;
            }
            
            return true;
        }

        public void EndDispatchMessage(System.IAsyncResult ar){
            try {
                SecureTcpState state = (SecureTcpState) ar.AsyncState;
                state.stream.EndWrite(ar);                
            }catch (System.Exception e) {
            // handle the error
                }
            try {
                _sendLock.Release();
            }
            catch { }
        }

        public void Dispose(){
            if (System.Threading.Interlocked.Increment(ref disposed) == 1){
                if (client != null){
                    client.Close();
                    client = null;
                }
                GC.SuppressFinalize(this);
            }
        }

        public uint keepAlive{
            get { return _keepAlive; }
            set { _keepAlive = value; }
        }
    }

2. This is created with a callback method to handle certificate validation,
then hook up the event handlers for the onReceived and onReceivedError
events (note it is all async):
     
_client = new SecureTcpClient(new
SecureConnectionResultsCallback(OnClientConnectionAvailable),
                    certValidationCallback, SslProtocols.Ssl3  );
_client.onReceived += new ReceivedDataEvent(_client_onReceived);
_client.onReceiveError += new ReceiveErrorEvent(_client_onReceiveError);
 
3. You can respond to whatever validation you like in the certificate check
- even none (bad idea!).
Remember that the certificate errors callback must be static.

static bool certificateErrorsCallback(object sender,X509Certificate
certificate, X509Chain chain,
        SslPolicyErrors sslPolicyErrors){
            if ((sslPolicyErrors == SslPolicyErrors.None)){
                // all ok               
                }
             
                if ( (sslPolicyErrors ==
SslPolicyErrors.RemoteCertificateNameMismatch) ||
                (sslPolicyErrors ==
SslPolicyErrors.RemoteCertificateChainErrors)) {
                // stuff for the certificate chain here;
             }

            if (sslPolicyErrors != SslPolicyErrors.None) {
                   // other checks in here
                if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateChainErrors) != 0) {
                    foreach (X509ChainStatus chainStatus in
chain.ChainStatus) {
                        // chain status errors
                    }
                }
            }
            //returning true tells the SslStream object you don't care about
any errors.
            return true;
        }

4. this is just the low level SSL communications. Above that you have all
the telnet negotiation and terminal processing.. which is where all the real
work goes.

5. Also remember the Universe secure telnet deamon runs on port 992 which
may be firewalled. 

Brian


_______________________________________________
U2-Users mailing list
U2-Users@listserver.u2ug.org
http://listserver.u2ug.org/mailman/listinfo/u2-users

Reply via email to