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