// AsyncSocket.cs // //XMPP .NET Library Copyright (C) 2006, 2008 Dieter Lunn // //This library is free software; you can redistribute it and/or modify it under //the terms of the GNU Lesser General Public License as published by the Free //Software Foundation; either version 3 of the License, or (at your option) //any later version. //This library is distributed in the hope that it will be useful, but WITHOUT //ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS //FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more // //You should have received a copy of the GNU Lesser General Public License along //with this library; if not, write to the Free Software Foundation, Inc., 59 //Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.IO; using System.Net; using System.Net.Security; using System.Security.Authentication; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using xmpp.logging; using xmpp; using xmpp.states; using xmpp.registries; namespace xmpp.net { /// /// AsyncSocket is the class that communicates with the server. /// public class AsyncSocket { private Socket _socket; private UTF8Encoding _utf = new UTF8Encoding(); private Address _dest; private byte[] _buff = new byte[4096]; private Stream _stream; private string _hostname; private bool _ssl; private bool _secure; private NetworkStream _netstream; private int _port; private ProtocolState _states = ProtocolState.Instance; // Used to determine if we are encrypting the socket to turn off returning the message to the parser private bool _encrypting = false; private SslStream _sslstream; //private ManualResetEvent _resetEvent = new ManualResetEvent(false); /// /// Initializes a new instance of the class. /// public AsyncSocket() { } /// /// Establishes a connection to the specified remote host. /// /// True if we connected, false if we didn't public bool Connect() { _dest = Address.Resolve(_hostname, _port); Logger.InfoFormat(this, "Connecting to: {0} on port {1}", _dest.IP.ToString(), _port.ToString()); _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { _socket.Connect(_dest.EndPoint); } catch (SocketException) { //We Failed to connect //TODO: Return an error using the Errors class so that the hosting application can take action. } if (_socket.Connected) { _netstream = new NetworkStream(_socket, true); _stream = _netstream; _stream.BeginRead(_buff, 0, _buff.Length, new AsyncCallback(Receive), null); _states.State = new ConnectedState(); _states.Execute(null); return true; } return false; } /// /// Encrypts the connection using SSL/TLS /// public void StartSecure() { //_encrypting = true; Logger.Debug(this, "Starting .NET Secure Mode"); _sslstream = new SslStream(_stream, true, new RemoteCertificateValidationCallback(RemoteValidation)); Logger.Debug(this, "Authenticating as Client"); try { _sslstream.AuthenticateAsClient(_dest.Hostname, null, SslProtocols.Tls, false); if (_sslstream.IsAuthenticated) { _stream = _sslstream; //_resetEvent.Set(); } //_resetEvent.WaitOne(); } catch (Exception e) { Logger.ErrorFormat(this, "SSL Error: {0}", e); } //_encrypting = false; } /* private void EndAuthenticate(IAsyncResult result) { _sslstream.EndAuthenticateAsClient(result); if (_sslstream.IsAuthenticated) { _stream = _sslstream; _resetEvent.Set(); } } */ private static bool RemoteValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors) { if (errors == SslPolicyErrors.None) { return true; } Logger.DebugFormat(typeof(AsyncSocket), "Policy Errors: {0}", errors); return false; } /// /// Closes the current socket. /// public void Close() { Logger.Debug(this, "Closing socket (Graceful Shutdown)"); _stream.Close(); _socket.Close(); } /// /// Writes data to the current connection. /// /// Message to send public void Write(string msg) { Logger.DebugFormat(this, "Outgoing Message: {0}", msg); byte[] mesg = _utf.GetBytes(msg); _stream.Write(mesg, 0, mesg.Length); } private void Receive(IAsyncResult ar) { try { Logger.Debug(this, ar.GetType().FullName); int rx = _stream.EndRead(ar); _stream.BeginRead(_buff, 0, _buff.Length, new AsyncCallback(Receive), null); if (!_encrypting) ProtocolParser.Parse(_buff, rx); } catch (SocketException e) { Logger.DebugFormat(this, "Socket Exception: {0}", e); } catch (InvalidOperationException e) { Logger.DebugFormat(this, "Invalid Operation: {0}", e); } catch (Exception e) { Logger.Error(this, e); } } /// /// /// /// public void StartCompression(string algorithm) { Logger.DebugFormat(this, "Replacing stream with {0} compressed version.", algorithm); _stream = CompressionRegistry.Instance.GetCompression(algorithm, _stream); Logger.Debug(this, _stream.GetType().Name); } /// /// Gets the current status of the socket. /// public bool Connected { get { return _socket.Connected; } } /// /// /// public string Hostname { get { return _hostname; } set { _hostname = value; } } /// /// /// public bool SSL { get { return _ssl; } set { _ssl = value; } } /// /// /// public bool Secure { get { return _secure; } set { _secure = value; } } public int Port { get { return _port; } set { _port = value; } } } }