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));
}
}
}