using System; using System.Text; using System.Collections; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; namespace libsecondlife { public delegate void PacketCallback(Packet packet); internal class AcceptAllCertificatePolicy : ICertificatePolicy { public AcceptAllCertificatePolicy() { } public bool CheckValidationResult(ServicePoint sPoint, System.Security.Cryptography.X509Certificates.X509Certificate cert, WebRequest wRequest,int certProb) { // Always accept return true; } } public class Circuit { public uint CircuitCode; private ProtocolManager Protocol; private NetworkManager Network; private byte[] Buffer; private Socket Connection; private IPEndPoint ipEndPoint; private EndPoint endPoint; public Circuit(ProtocolManager protocol, NetworkManager network, uint circuitCode) { Protocol = protocol; Network = network; CircuitCode = circuitCode; Buffer = new byte[4096]; Connection = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); } public bool Open(string ip, int port) { // Setup the callback AsyncCallback onReceivedData = new AsyncCallback(this.OnRecievedData); // Create an endpoint that we will be communicating with (need it in two types due to // .NET weirdness) ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); endPoint = (EndPoint)ipEndPoint; // Associate this circuit's socket with the given ip and port and start listening Connection.Connect(endPoint); Connection.BeginReceiveFrom(Buffer, 0, Buffer.Length, SocketFlags.None, ref endPoint, onReceivedData, null); // Send the UseCircuitCode packet to initiate the connection Packet packet = PacketBuilder.UseCircuitCode(Protocol, Network.LoginValues.AgentID, Network.LoginValues.SessionID, CircuitCode); Connection.Send((byte[])packet.Data.ToArray(typeof(Byte))); return false; } public void Close() { //FIXME: CloseCircuit Connection.Close(); } private void OnRecievedData(IAsyncResult result) { int numBytes = Connection.EndReceiveFrom(result, ref endPoint); Packet packet = new Packet(Buffer, numBytes, Protocol); PacketCallback callback = (PacketCallback)Network.Callbacks[packet.Layout.Name]; callback(packet); } } public struct LoginReply { public LLUUID SessionID; public LLUUID SecureSessionID; public string StartLocation; public string FirstName; public string LastName; public int RegionX; public int RegionY; public string Home; public string Message; public uint CircuitCode; public int Port; public string IP; public string LookAt; public LLUUID AgentID; public uint SecondsSinceEpoch; } public class NetworkManager { public LoginReply LoginValues; public string LoginError; public Hashtable Callbacks; private ProtocolManager Protocol; private string LoginBuffer; private ArrayList Circuits; private Hashtable InternalCallbacks; public NetworkManager(ProtocolManager protocol) { Protocol = protocol; Circuits = new ArrayList(); Callbacks = new Hashtable(); } public bool Login(string firstName, string lastName, string password, string mac, int major, int minor, int patch, int build, string platform, string viewerDigest, string userAgent, string author) { return Login(firstName, lastName, password, mac, major, minor, patch, build, platform, viewerDigest, userAgent, author, "https://login.agni.lindenlab.com/cgi-bin/login.cgi"); } public bool Login(string firstName, string lastName, string password, string mac, int major, int minor, int patch, int build, string platform, string viewerDigest, string userAgent, string author, string url) { WebRequest login; WebResponse response; // Generate an MD5 hash of the password MD5 md5 = new MD5CryptoServiceProvider(); byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(password)); StringBuilder passwordDigest = new StringBuilder(); // Convert the hash to a hex string foreach(byte b in hash) { passwordDigest.AppendFormat("{0:x2}", b); } string loginRequest = "login_to_simulator" + "" + "first" + firstName + "" + "last" + lastName + "" + "passwd$1$" + passwordDigest + "" + "startlast" + "major" + major + "" + "minor" + minor + "" + "patch" + patch + "" + "build" + build + "" + "platform" + platform + "" + "mac" + mac + "" + "viewer_digest" + viewerDigest + "" + "user-agent" + userAgent + " (" + Helpers.VERSION + ")" + "author" + author + "" + "" ; try { // Override SSL authentication mechanisms ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy(); login = WebRequest.Create(url); login.ContentType = "text/xml"; login.Method = "POST"; login.Timeout = 12000; byte[] request = System.Text.Encoding.ASCII.GetBytes(loginRequest); login.ContentLength = request.Length; System.IO.Stream stream = login.GetRequestStream(); stream.Write(request, 0, request.Length); stream.Close(); response = login.GetResponse(); if (response == null) { LoginError = "Error logging in: (Unknown)"; Helpers.Log(LoginError, Helpers.LogLevel.Warning); return false; } //TODO: To support UTF8 avatar names the encoding should be handled better System.IO.StreamReader streamReader = new System.IO.StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII); LoginBuffer = streamReader.ReadToEnd(); streamReader.Close(); response.Close(); } catch (Exception e) { LoginError = "Error logging in: " + e.Message; Helpers.Log(LoginError, Helpers.LogLevel.Warning); return false; } if (!ParseLoginReply()) { return false; } // Connect to the sim given in the login reply Circuit circuit = new Circuit(Protocol, this, LoginValues.CircuitCode); circuit.Open(LoginValues.IP, LoginValues.Port); return true; } private bool ParseLoginReply() { string msg; msg = RpcGetString(LoginBuffer, "reason"); if (msg.Length != 0) { LoginError = RpcGetString(LoginBuffer, "message"); return false; } msg = RpcGetString(LoginBuffer, "logintrue"); if (msg.Length == 0) { LoginError = "Unknown login error"; return false; } // Grab the login parameters LoginValues.SessionID = RpcGetString(LoginBuffer.ToString(), "session_id"); LoginValues.SecureSessionID = RpcGetString(LoginBuffer.ToString(), "secure_session_id"); LoginValues.StartLocation = RpcGetString(LoginBuffer.ToString(), "start_location"); LoginValues.FirstName = RpcGetString(LoginBuffer.ToString(), "first_name"); LoginValues.LastName = RpcGetString(LoginBuffer.ToString(), "last_name"); LoginValues.RegionX = RpcGetInt(LoginBuffer.ToString(), "region_x"); LoginValues.RegionY = RpcGetInt(LoginBuffer.ToString(), "region_y"); LoginValues.Home = RpcGetString(LoginBuffer.ToString(), "home"); LoginValues.Message = RpcGetString(LoginBuffer.ToString(), "message").Replace("\r\n", ""); LoginValues.CircuitCode = (uint)RpcGetInt(LoginBuffer.ToString(), "circuit_code"); LoginValues.Port = RpcGetInt(LoginBuffer.ToString(), "sim_port"); LoginValues.IP = RpcGetString(LoginBuffer.ToString(), "sim_ip"); LoginValues.LookAt = RpcGetString(LoginBuffer.ToString(), "look_at"); LoginValues.AgentID = RpcGetString(LoginBuffer.ToString(), "agent_id"); LoginValues.SecondsSinceEpoch = (uint)RpcGetInt(LoginBuffer.ToString(), "seconds_since_epoch"); return true; } string RpcGetString(string rpc, string name) { int pos = rpc.IndexOf(name); int pos2; if (pos == -1) { return ""; } rpc = rpc.Substring(pos, rpc.Length - pos); pos = rpc.IndexOf(""); if (pos == -1) { return ""; } rpc = rpc.Substring(pos + 8, rpc.Length - (pos + 8)); pos2 = rpc.IndexOf(""); if (pos2 == -1) { return ""; } return rpc.Substring(0, pos2); } int RpcGetInt(string rpc, string name) { int pos = rpc.IndexOf(name); int pos2; if (pos == -1) { return -1; } rpc = rpc.Substring(pos, rpc.Length - pos); pos = rpc.IndexOf(""); if (pos == -1) { return -1; } rpc = rpc.Substring(pos + 4, rpc.Length - (pos + 4)); pos2 = rpc.IndexOf(""); if (pos2 == -1) { return -1; } return Int32.Parse(rpc.Substring(0, pos2)); } } }