/old/libsl1550/SLProxy/SLProxy.cs
C# | 2013 lines | 1566 code | 247 blank | 200 comment | 252 complexity | dfcd0dd9a9c7d4043de480f24a0db18a MD5 | raw file
Possible License(s): Apache-2.0, BSD-2-Clause, MIT, LGPL-2.1, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, GPL-3.0, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * SLProxy.cs: implementation of Second Life proxy library
- *
- * Copyright (c) 2006 Austin Jennings
- * Pregen modifications made by Andrew Ortman on Dec 10, 2006 -> Dec 20, 2006
- *
- *
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the Second Life Reverse Engineering Team nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
- // #define DEBUG_SEQUENCE
- // #define DEBUG_CAPS
- // #define DEBUG_THREADS
- using Nwc.XmlRpc;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Xml;
- using libsecondlife;
- using libsecondlife.StructuredData;
- using libsecondlife.Packets;
- // SLProxy: proxy library for Second Life
- namespace SLProxy
- {
- // ProxyConfig: configuration for proxy objects
- public class ProxyConfig
- {
- // userAgent: name of the proxy application
- public string userAgent;
- // author: email address of the proxy application's author
- public string author;
- // loginPort: port that the login proxy will listen on
- public ushort loginPort = 8080;
- // clientFacingAddress: address from which to communicate with the client
- public IPAddress clientFacingAddress = IPAddress.Loopback;
- // remoteFacingAddress: address from which to communicate with the server
- public IPAddress remoteFacingAddress = IPAddress.Any;
- // remoteLoginUri: URI for Second Life's login server
- public Uri remoteLoginUri = new Uri("https://login.agni.lindenlab.com/cgi-bin/login.cgi");
- // verbose: whether or not to print informative messages
- public bool verbose = true;
- // ProxyConfig: construct a default proxy configuration with the specified userAgent, author, and protocol
- public ProxyConfig(string userAgent, string author)
- {
- this.userAgent = userAgent;
- this.author = author;
- }
- // ProxyConfig: construct a default proxy configuration, parsing command line arguments (try --proxy-help)
- public ProxyConfig(string userAgent, string author, string[] args)
- : this(userAgent, author)
- {
- Dictionary<string, ArgumentParser> argumentParsers = new Dictionary<string, ArgumentParser>();
- argumentParsers["proxy-help"] = new ArgumentParser(ParseHelp);
- argumentParsers["proxy-login-port"] = new ArgumentParser(ParseLoginPort);
- argumentParsers["proxy-client-facing-address"] = new ArgumentParser(ParseClientFacingAddress);
- argumentParsers["proxy-remote-facing-address"] = new ArgumentParser(ParseRemoteFacingAddress);
- argumentParsers["proxy-remote-login-uri"] = new ArgumentParser(ParseRemoteLoginUri);
- argumentParsers["proxy-verbose"] = new ArgumentParser(ParseVerbose);
- argumentParsers["proxy-quiet"] = new ArgumentParser(ParseQuiet);
- foreach (string arg in args)
- foreach (string argument in argumentParsers.Keys)
- {
- Match match = (new Regex("^--" + argument + "(?:=(.*))?$")).Match(arg);
- if (match.Success)
- {
- string value;
- if (match.Groups[1].Captures.Count == 1)
- value = match.Groups[1].Captures[0].ToString();
- else
- value = null;
- try
- {
- ((ArgumentParser)argumentParsers[argument])(value);
- }
- catch
- {
- Console.WriteLine("invalid value for --" + argument);
- ParseHelp(null);
- }
- }
- }
- }
- private delegate void ArgumentParser(string value);
- private void ParseHelp(string value)
- {
- Console.WriteLine("Proxy command-line arguments:");
- Console.WriteLine(" --proxy-help display this help");
- Console.WriteLine(" --proxy-login-port=<port> listen for logins on <port>");
- Console.WriteLine(" --proxy-client-facing-address=<IP> communicate with client via <IP>");
- Console.WriteLine(" --proxy-remote-facing-address=<IP> communicate with server via <IP>");
- Console.WriteLine(" --proxy-remote-login-uri=<URI> use SL login server at <URI>");
- Console.WriteLine(" --proxy-verbose display proxy notifications");
- Console.WriteLine(" --proxy-quiet suppress proxy notifications");
- Environment.Exit(1);
- }
- private void ParseLoginPort(string value)
- {
- loginPort = Convert.ToUInt16(value);
- }
- private void ParseClientFacingAddress(string value)
- {
- clientFacingAddress = IPAddress.Parse(value);
- }
- private void ParseRemoteFacingAddress(string value)
- {
- remoteFacingAddress = IPAddress.Parse(value);
- }
- private void ParseRemoteLoginUri(string value)
- {
- remoteLoginUri = new Uri(value);
- }
- private void ParseVerbose(string value)
- {
- if (value != null)
- throw new Exception();
- verbose = true;
- }
- private void ParseQuiet(string value)
- {
- if (value != null)
- throw new Exception();
- verbose = false;
- }
- }
- // Proxy: Second Life proxy server
- // A Proxy instance is only prepared to deal with one client at a time.
- public class Proxy
- {
- private ProxyConfig proxyConfig;
- private string loginURI;
- /*
- * Proxy Management
- */
- // Proxy: construct a proxy server with the given configuration
- public Proxy(ProxyConfig proxyConfig)
- {
- this.proxyConfig = proxyConfig;
- InitializeLoginProxy();
- InitializeSimProxy();
- InitializeCaps();
- }
- object keepAliveLock = new Object();
- // Start: begin accepting clients
- public void Start()
- {
- lock (this)
- {
- System.Threading.Monitor.Enter(keepAliveLock);
- (new Thread(new ThreadStart(KeepAlive))).Start();
- RunSimProxy();
- Thread runLoginProxy = new Thread(new ThreadStart(RunLoginProxy));
- runLoginProxy.IsBackground = true;
- runLoginProxy.Start();
- IPEndPoint endPoint = (IPEndPoint)loginServer.LocalEndPoint;
- IPAddress displayAddress;
- if (endPoint.Address == IPAddress.Any)
- displayAddress = IPAddress.Loopback;
- else
- displayAddress = endPoint.Address;
- loginURI = "http://" + displayAddress + ":" + endPoint.Port + "/";
- Log("proxy ready at " + loginURI, false);
- }
- }
- // Stop: allow foreground threads to die
- public void Stop()
- {
- lock (this)
- {
- System.Threading.Monitor.Exit(keepAliveLock);
- }
- }
- // KeepAlive: blocks until the proxy is free to shut down
- public void KeepAlive()
- {
- #if DEBUG_THREADS
- Console.WriteLine(">T> KeepAlive");
- #endif
- lock (keepAliveLock) { };
- #if DEBUG_THREADS
- Console.WriteLine("<T< KeepAlive");
- #endif
- }
- // SetLoginRequestDelegate: specify a callback loginRequestDelegate that will be called when the client requests login
- public void SetLoginRequestDelegate(XmlRpcRequestDelegate loginRequestDelegate)
- {
- lock (this)
- {
- this.loginRequestDelegate = loginRequestDelegate;
- }
- }
- // SetLoginResponseDelegate: specify a callback loginResponseDelegate that will be called when the server responds to login
- public void SetLoginResponseDelegate(XmlRpcResponseDelegate loginResponseDelegate)
- {
- lock (this)
- {
- this.loginResponseDelegate = loginResponseDelegate;
- }
- }
- // AddDelegate: add callback packetDelegate for packets of type packetName going direction
- public void AddDelegate(PacketType packetType, Direction direction, PacketDelegate packetDelegate)
- {
- lock (this)
- {
- Dictionary<PacketType, List<PacketDelegate>> delegates = (direction == Direction.Incoming ? incomingDelegates : outgoingDelegates);
- if (!delegates.ContainsKey(packetType))
- {
- delegates[packetType] = new List<PacketDelegate>();
- }
- List<PacketDelegate> delegateArray = delegates[packetType];
- if (!delegateArray.Contains(packetDelegate))
- {
- delegateArray.Add(packetDelegate);
- }
- }
- }
- // RemoveDelegate: remove callback for packets of type packetName going direction
- public void RemoveDelegate(PacketType packetType, Direction direction, PacketDelegate packetDelegate)
- {
- lock (this)
- {
- Dictionary<PacketType, List<PacketDelegate>> delegates = (direction == Direction.Incoming ? incomingDelegates : outgoingDelegates);
- if (!delegates.ContainsKey(packetType))
- {
- return;
- }
- List<PacketDelegate> delegateArray = delegates[packetType];
- if (delegateArray.Contains(packetDelegate))
- {
- delegateArray.Remove(packetDelegate);
- }
- }
- }
- private Packet callDelegates(Dictionary<PacketType, List<PacketDelegate>> delegates, Packet packet, IPEndPoint remoteEndPoint)
- {
- PacketType origType = packet.Type;
- foreach (PacketDelegate del in delegates[origType])
- {
- packet = del(packet, remoteEndPoint);
- // FIXME: how should we handle the packet type changing?
- if (packet == null || packet.Type != origType) break;
- }
- return packet;
- }
- // InjectPacket: send packet to the client or server when direction is Incoming or Outgoing, respectively
- public void InjectPacket(Packet packet, Direction direction)
- {
- lock (this)
- {
- if (activeCircuit == null)
- {
- // no active circuit; queue the packet for injection once we have one
- List<Packet> queue = direction == Direction.Incoming ? queuedIncomingInjections : queuedOutgoingInjections;
- queue.Add(packet);
- }
- else
- // tell the active sim proxy to inject the packet
- ((SimProxy)simProxies[activeCircuit]).Inject(packet, direction);
- }
- }
- // Log: write message to the console if in verbose mode
- private void Log(object message, bool important)
- {
- if (proxyConfig.verbose || important)
- Console.WriteLine(message);
- }
- /*
- * Login Proxy
- */
- private Socket loginServer;
- private int capsReqCount = 0;
- // InitializeLoginProxy: initialize the login proxy
- private void InitializeLoginProxy()
- {
- loginServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- loginServer.Bind(new IPEndPoint(proxyConfig.clientFacingAddress, proxyConfig.loginPort));
- loginServer.Listen(1);
- }
- // RunLoginProxy: process login requests from clients
- private void RunLoginProxy()
- {
- #if DEBUG_THREADS
- Console.WriteLine(">T> RunLoginProxy");
- #endif
- try
- {
- for (; ; )
- {
- Socket client = loginServer.Accept();
- IPEndPoint clientEndPoint = (IPEndPoint)client.RemoteEndPoint;
- try
- {
- Thread connThread = new Thread((ThreadStart)delegate
- {
- #if DEBUG_THREADS
- Console.WriteLine(">T> ProxyHTTP");
- #endif
- ProxyHTTP(client);
- #if DEBUG_THREADS
- Console.WriteLine("<T< ProxyHTTP");
- } catch (Exception e) {
- Console.WriteLine("ProxyHTTP: {0}", e.Message);
- }
- #endif
- });
- connThread.IsBackground = true;
- connThread.Start();
- }
- catch (Exception e)
- {
- Log("login failed: " + e.Message, false);
- }
- // send any packets queued for injection
- if (activeCircuit != null) lock (this)
- {
- SimProxy activeProxy = (SimProxy)simProxies[activeCircuit];
- foreach (Packet packet in queuedOutgoingInjections)
- activeProxy.Inject(packet, Direction.Outgoing);
- queuedOutgoingInjections = new List<Packet>();
- }
- }
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- Console.WriteLine(e.StackTrace);
- }
- #if DEBUG_THREADS
- Console.WriteLine("<T< RunLoginProxy");
- #endif
- }
- private class HandyNetReader
- {
- private NetworkStream netStream;
- private const int BUF_SIZE = 8192;
- private byte[] buf = new byte[BUF_SIZE];
- private int bufFill = 0;
- public HandyNetReader(NetworkStream s)
- {
- netStream = s;
- }
- public byte[] ReadLine()
- {
- int i = -1;
- while (true)
- {
- i = Array.IndexOf(buf, (byte)'\n', 0, bufFill);
- if (i >= 0) break;
- if (bufFill >= BUF_SIZE) return null;
- if (!ReadMore()) return null;
- }
- byte[] ret = new byte[i];
- Array.Copy(buf, ret, i);
- Array.Copy(buf, i + 1, buf, 0, bufFill - (i + 1));
- bufFill -= i + 1;
- return ret;
- }
- private bool ReadMore()
- {
- int n = netStream.Read(buf, bufFill, BUF_SIZE - bufFill);
- bufFill += n;
- return n > 0;
- }
- public int Read(byte[] rbuf, int start, int len)
- {
- int read = 0;
- while (len > bufFill)
- {
- Array.Copy(buf, 0, rbuf, start, bufFill);
- start += bufFill; len -= bufFill;
- read += bufFill; bufFill = 0;
- if (!ReadMore()) break;
- }
- Array.Copy(buf, 0, rbuf, start, len);
- Array.Copy(buf, len, buf, 0, bufFill - len);
- bufFill -= len; read += len;
- return read;
- }
- }
- // ProxyHTTP: proxy a HTTP request
- private void ProxyHTTP(Socket client)
- {
- NetworkStream netStream = new NetworkStream(client);
- HandyNetReader reader = new HandyNetReader(netStream);
- string line = null;
- int reqNo;
- int contentLength = 0;
- Match match;
- string uri;
- string meth;
- Dictionary<string, string> headers = new Dictionary<string, string>();
- lock (this)
- {
- capsReqCount++; reqNo = capsReqCount;
- }
- byte[] byteLine = reader.ReadLine();
- if (byteLine != null) line = Encoding.UTF8.GetString(byteLine).Replace("\r", "");
- if (line == null)
- throw new Exception("EOF in client HTTP header");
- match = new Regex(@"^(\S+)\s+(\S+)\s+(HTTP/\d\.\d)$").Match(line);
- if (!match.Success)
- {
- Console.WriteLine("[" + reqNo + "] Bad request!");
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n");
- netStream.Write(wr, 0, wr.Length);
- netStream.Close(); client.Close();
- return;
- }
- meth = match.Groups[1].Captures[0].ToString();
- uri = match.Groups[2].Captures[0].ToString();
- #if DEBUG_CAPS
- Console.WriteLine("[" + reqNo + "] " + meth + ": " + uri); // FIXME - for debugging only
- #endif
- // read HTTP header
- do
- {
- // read one line of the header
- line = Encoding.UTF8.GetString(reader.ReadLine()).Replace("\r", "");
- // check for premature EOF
- if (line == null)
- throw new Exception("EOF in client HTTP header");
- if (line == "") break;
- match = new Regex(@"^([^:]+):\s*(.*)$").Match(line);
- if (!match.Success)
- {
- Console.WriteLine("[" + reqNo + "] Bad header: '" + line + "'");
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n");
- netStream.Write(wr, 0, wr.Length);
- netStream.Close(); client.Close();
- return;
- }
- string key = match.Groups[1].Captures[0].ToString();
- string val = match.Groups[2].Captures[0].ToString();
- headers[key.ToLower()] = val;
- } while (line != "");
- if (headers.ContainsKey("content-length"))
- {
- contentLength = Convert.ToInt32(headers["content-length"]);
- }
- // read the HTTP body into a buffer
- byte[] content = new byte[contentLength];
- reader.Read(content, 0, contentLength);
- #if DEBUG_CAPS
- if (contentLength < 8192) Console.WriteLine("[" + reqNo + "] request length = " + contentLength + ":\n" + Helpers.FieldToUTF8String(content) + "\n-------------");
- #endif
- if (uri == "/")
- {
- ProxyLogin(netStream, content);
- }
- else if (new Regex(@"^/https?://.*$").Match(uri).Success)
- {
- ProxyCaps(netStream, meth, uri.Substring(1), headers, content, reqNo);
- }
- else
- {
- Console.WriteLine("404 not found: " + uri);
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n");
- netStream.Write(wr, 0, wr.Length);
- netStream.Close(); client.Close();
- return;
- }
- netStream.Close();
- client.Close();
- }
- private Dictionary<string, CapInfo> KnownCaps;
- private Dictionary<string, bool> SubHack = new Dictionary<string, bool>();
- private void ProxyCaps(NetworkStream netStream, string meth, string uri, Dictionary<string, string> headers, byte[] content, int reqNo)
- {
- Match match = new Regex(@"^(https?)://([^:/]+)(:\d+)?(/.*)$").Match(uri);
- if (!match.Success)
- {
- Console.WriteLine("[" + reqNo + "] Malformed proxy URI: " + uri);
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n");
- netStream.Write(wr, 0, wr.Length);
- return;
- }
- //DEBUG: Console.WriteLine("[" + reqNo + "] : " + meth);
- CapInfo cap = null;
- lock (this)
- {
- if (KnownCaps.ContainsKey(uri))
- {
- cap = KnownCaps[uri];
- }
- }
- CapsRequest capReq = null; bool shortCircuit = false; bool requestFailed = false;
- if (cap != null)
- {
- capReq = new CapsRequest(cap);
- if (cap.ReqFmt == CapsDataFormat.LLSD)
- {
- capReq.Request = LLSDParser.DeserializeXml(content);
- }
- else
- {
- capReq.Request = content;
- }
- foreach (CapsDelegate d in cap.GetDelegates())
- {
- if (d(capReq, CapsStage.Request)) { shortCircuit = true; break; }
- }
- if (cap.ReqFmt == CapsDataFormat.LLSD)
- {
- content = LLSDParser.SerializeXmlBytes((LLSD)capReq.Request);
- }
- else
- {
- content = (byte[])capReq.Request;
- }
- }
- byte[] respBuf = null;
- string consoleMsg = String.Empty;
- if (shortCircuit)
- {
- byte[] wr = Encoding.UTF8.GetBytes("HTTP/1.0 200 OK\r\n");
- netStream.Write(wr, 0, wr.Length);
- }
- else
- {
- HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
- req.KeepAlive = false;
- foreach (string header in headers.Keys)
- {
- if (header == "accept" || header == "connection" ||
- header == "content-length" || header == "date" || header == "expect" ||
- header == "host" || header == "if-modified-since" || header == "referer" ||
- header == "transfer-encoding" || header == "user-agent" ||
- header == "proxy-connection")
- {
- // can't touch these!
- }
- else if (header == "content-type")
- {
- req.ContentType = headers["content-type"];
- }
- else
- {
- req.Headers[header] = headers[header];
- }
- }
- req.Method = meth;
- req.ContentLength = content.Length;
- HttpWebResponse resp;
- try
- {
- Stream reqStream = req.GetRequestStream();
- reqStream.Write(content, 0, content.Length);
- reqStream.Close();
- resp = (HttpWebResponse)req.GetResponse();
- }
- catch (WebException e)
- {
- if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.SendFailure)
- {
- Console.WriteLine("Request timeout");
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 504 Proxy Request Timeout\r\nContent-Length: 0\r\n\r\n");
- netStream.Write(wr, 0, wr.Length);
- return;
- }
- else if (e.Status == WebExceptionStatus.ProtocolError && e.Response != null)
- {
- resp = (HttpWebResponse)e.Response; requestFailed = true;
- }
- else
- {
- Console.WriteLine("Request error: " + e.ToString());
- byte[] wr = Encoding.ASCII.GetBytes("HTTP/1.0 502 Gateway Error\r\nContent-Length: 0\r\n\r\n"); // FIXME
- netStream.Write(wr, 0, wr.Length);
- return;
- }
- }
- try
- {
- Stream respStream = resp.GetResponseStream();
- int read;
- int length = 0;
- respBuf = new byte[256];
- do
- {
- read = respStream.Read(respBuf, length, 256);
- if (read > 0)
- {
- length += read;
- Array.Resize(ref respBuf, length + 256);
- }
- } while (read > 0);
- Array.Resize(ref respBuf, length);
- if (capReq != null && !requestFailed)
- {
- if (cap.RespFmt == CapsDataFormat.LLSD)
- {
- capReq.Response = LLSDParser.DeserializeXml(respBuf);
- }
- else
- {
- capReq.Response = respBuf;
- }
- }
- consoleMsg += "[" + reqNo + "] Response from " + uri + "\nStatus: " + (int)resp.StatusCode + " " + resp.StatusDescription + "\n";
- {
- byte[] wr = Encoding.UTF8.GetBytes("HTTP/1.0 " + (int)resp.StatusCode + " " + resp.StatusDescription + "\r\n");
- netStream.Write(wr, 0, wr.Length);
- }
- for (int i = 0; i < resp.Headers.Count; i++)
- {
- string key = resp.Headers.Keys[i];
- string val = resp.Headers[i];
- string lkey = key.ToLower();
- if (lkey != "content-length" && lkey != "transfer-encoding" && lkey != "connection")
- {
- consoleMsg += key + ": " + val + "\n";
- byte[] wr = Encoding.UTF8.GetBytes(key + ": " + val + "\r\n");
- netStream.Write(wr, 0, wr.Length);
- }
- }
- }
- catch (Exception)
- {
- // TODO: Should we handle this somehow?
- }
- }
- if (cap != null && !requestFailed)
- {
- foreach (CapsDelegate d in cap.GetDelegates())
- {
- try
- {
- if (d(capReq, CapsStage.Response)) { break; }
- }
- catch (Exception e)
- {
- Console.WriteLine(e.ToString()); break;
- }
- }
- if (cap.RespFmt == CapsDataFormat.LLSD)
- {
- respBuf = LLSDParser.SerializeXmlBytes((LLSD)capReq.Response);
- }
- else
- {
- respBuf = (byte[])capReq.Response;
- }
- }
- consoleMsg += "\n" + Encoding.UTF8.GetString(respBuf) + "\n--------";
- #if DEBUG_CAPS
- Console.WriteLine(consoleMsg);
- #endif
- /* try {
- if(resp.StatusCode == HttpStatusCode.OK) respBuf = CapsFixup(uri,respBuf);
- } catch(Exception e) {
- Console.WriteLine("["+reqNo+"] Couldn't fixup response: "+e.ToString());
- } */
- #if DEBUG_CAPS
- Console.WriteLine("[" + reqNo + "] Fixed-up response:\n" + Encoding.UTF8.GetString(respBuf) + "\n--------");
- #endif
- try
- {
- byte[] wr2 = Encoding.UTF8.GetBytes("Content-Length: " + respBuf.Length + "\r\n\r\n");
- netStream.Write(wr2, 0, wr2.Length);
- netStream.Write(respBuf, 0, respBuf.Length);
- }
- catch (Exception e)
- {
- Console.WriteLine(e.ToString());
- }
- return;
- }
- private bool FixupSeedCapsResponse(CapsRequest capReq, CapsStage stage)
- {
- if (stage != CapsStage.Response) return false;
- Dictionary<string, object> m = (Dictionary<string, object>)capReq.Response;
- Dictionary<string, object> nm = new Dictionary<string, object>();
- foreach (string key in m.Keys)
- {
- string val = (string)m[key];
- if (!String.IsNullOrEmpty(val))
- {
- if (!KnownCaps.ContainsKey(val))
- {
- CapInfo newCap = new CapInfo(val, capReq.Info.Sim, key);
- newCap.AddDelegate(new CapsDelegate(KnownCapDelegate));
- lock (this) { KnownCaps[val] = newCap; }
- }
- nm[key] = loginURI + val;
- }
- else
- {
- nm[key] = val;
- }
- }
- capReq.Response = nm;
- return false;
- }
- private Dictionary<string, List<CapsDelegate>> KnownCapsDelegates = new Dictionary<string, List<CapsDelegate>>();
- private void InitializeCaps()
- {
- AddCapsDelegate("EventQueueGet", new CapsDelegate(FixupEventQueueGet));
- }
- public void AddCapsDelegate(string CapName, CapsDelegate capsDelegate)
- {
- lock (this)
- {
- if (!KnownCapsDelegates.ContainsKey(CapName))
- {
- KnownCapsDelegates[CapName] = new List<CapsDelegate>();
- }
- List<CapsDelegate> delegateArray = KnownCapsDelegates[CapName];
- if (!delegateArray.Contains(capsDelegate))
- {
- delegateArray.Add(capsDelegate);
- }
- }
- }
- public void RemoveCapRequestDelegate(string CapName, CapsDelegate capsDelegate)
- {
- lock (this)
- {
- if (!KnownCapsDelegates.ContainsKey(CapName))
- {
- return;
- }
- List<CapsDelegate> delegateArray = KnownCapsDelegates[CapName];
- if (delegateArray.Contains(capsDelegate))
- {
- delegateArray.Remove(capsDelegate);
- }
- }
- }
- private bool KnownCapDelegate(CapsRequest capReq, CapsStage stage)
- {
- lock (this)
- {
- if (!KnownCapsDelegates.ContainsKey(capReq.Info.CapType))
- return false;
- List<CapsDelegate> delegates = KnownCapsDelegates[capReq.Info.CapType];
- foreach (CapsDelegate d in delegates)
- {
- if (d(capReq, stage)) { return true; }
- }
- }
- return false;
- }
- private bool FixupEventQueueGet(CapsRequest capReq, CapsStage stage)
- {
- if (stage != CapsStage.Response) return false;
- foreach (Dictionary<string, object> evt in (List<object>)((Dictionary<string, object>)capReq.Response)["events"])
- {
- string message = (string)evt["message"];
- Dictionary<string, object> body = (Dictionary<string, object>)evt["body"];
- if (message == "TeleportFinish" || message == "CrossedRegion")
- {
- Dictionary<string, object> info = null;
- if (message == "TeleportFinish")
- info = (Dictionary<string, object>)(((List<object>)body["Info"])[0]);
- else
- info = (Dictionary<string, object>)(((List<object>)body["RegionData"])[0]);
- byte[] bytes = (byte[])info["SimIP"];
- uint simIP = Helpers.BytesToUIntBig((byte[])info["SimIP"]);
- ushort simPort = (ushort)(int)info["SimPort"];
- string capsURL = (string)info["SeedCapability"];
- GenericCheck(ref simIP, ref simPort, ref capsURL, capReq.Info.Sim == activeCircuit);
- info["SeedCapability"] = capsURL;
- bytes[0] = (byte)(simIP % 256);
- bytes[1] = (byte)((simIP >> 8) % 256);
- bytes[2] = (byte)((simIP >> 16) % 256);
- bytes[3] = (byte)((simIP >> 24) % 256);
- info["SimIP"] = bytes;
- info["SimPort"] = (int)simPort;
- }
- else if (message == "EstablishAgentCommunication")
- {
- string ipAndPort = (string)body["sim-ip-and-port"];
- string[] pieces = ipAndPort.Split(':');
- byte[] bytes = IPAddress.Parse(pieces[0]).GetAddressBytes();
- uint simIP = (uint)(bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24));
- ushort simPort = (ushort)Convert.ToInt32(pieces[1]);
- string capsURL = (string)body["seed-capability"];
- Console.WriteLine("DEBUG: Got EstablishAgentCommunication for " + ipAndPort + " with seed cap " + capsURL);
- GenericCheck(ref simIP, ref simPort, ref capsURL, false);
- body["seed-capability"] = capsURL;
- body["sim-ip-and-port"] = new IPAddress((uint)simIP).ToString() + ":" + simPort;
- Console.WriteLine("DEBUG: Modified EstablishAgentCommunication to " + (string)body["sim-ip-and-port"] + " with seed cap " + capsURL);
- }
- }
- return false;
- }
- private void ProxyLogin(NetworkStream netStream, byte[] content)
- {
- lock (this)
- {
- // convert the body into an XML-RPC request
- XmlRpcRequest request = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(Encoding.UTF8.GetString(content));
- // call the loginRequestDelegate
- if (loginRequestDelegate != null)
- try
- {
- loginRequestDelegate(request);
- }
- catch (Exception e)
- {
- Log("exception in login request deligate: " + e.Message, true);
- Log(e.StackTrace, true);
- }
- // add our userAgent and author to the request
- System.Collections.Hashtable requestParams = new System.Collections.Hashtable();
- if (proxyConfig.userAgent != null)
- requestParams["user-agent"] = proxyConfig.userAgent;
- if (proxyConfig.author != null)
- requestParams["author"] = proxyConfig.author;
- request.Params.Add(requestParams);
- XmlRpcResponse response;
- try
- {
- // forward the XML-RPC request to the server
- response = (XmlRpcResponse)request.Send(proxyConfig.remoteLoginUri.ToString(),
- 60000); //added 60 second timeout -- Andrew
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- return;
- }
- System.Collections.Hashtable responseData = (System.Collections.Hashtable)response.Value;
- // proxy any simulator address given in the XML-RPC response
- if (responseData.Contains("sim_ip") && responseData.Contains("sim_port"))
- {
- IPEndPoint realSim = new IPEndPoint(IPAddress.Parse((string)responseData["sim_ip"]), Convert.ToUInt16(responseData["sim_port"]));
- IPEndPoint fakeSim = ProxySim(realSim);
- responseData["sim_ip"] = fakeSim.Address.ToString();
- responseData["sim_port"] = fakeSim.Port;
- activeCircuit = realSim;
- }
- // start a new proxy session
- Reset();
- if (responseData.Contains("seed_capability"))
- {
- CapInfo info = new CapInfo((string)responseData["seed_capability"], activeCircuit, "SeedCapability");
- info.AddDelegate(new CapsDelegate(FixupSeedCapsResponse));
- KnownCaps[(string)responseData["seed_capability"]] = info;
- responseData["seed_capability"] = loginURI + responseData["seed_capability"];
- }
- // call the loginResponseDelegate
- if (loginResponseDelegate != null)
- {
- try
- {
- loginResponseDelegate(response);
- }
- catch (Exception e)
- {
- Log("exception in login response delegate: " + e.Message, true);
- Log(e.StackTrace, true);
- }
- }
- // forward the XML-RPC response to the client
- StreamWriter writer = new StreamWriter(netStream);
- writer.Write("HTTP/1.0 200 OK\r\n");
- writer.Write("Content-type: text/xml\r\n");
- writer.Write("\r\n");
- XmlTextWriter responseWriter = new XmlTextWriter(writer);
- XmlRpcResponseSerializer.Singleton.Serialize(responseWriter, response);
- responseWriter.Close(); writer.Close();
- }
- }
- /*
- * Sim Proxy
- */
- private Socket simFacingSocket;
- private IPEndPoint activeCircuit = null;
- private Dictionary<IPEndPoint, IPEndPoint> proxyEndPoints = new Dictionary<IPEndPoint, IPEndPoint>();
- private Dictionary<IPEndPoint, SimProxy> simProxies = new Dictionary<IPEndPoint, SimProxy>();
- private Dictionary<EndPoint, SimProxy> proxyHandlers = new Dictionary<EndPoint, SimProxy>();
- private XmlRpcRequestDelegate loginRequestDelegate = null;
- private XmlRpcResponseDelegate loginResponseDelegate = null;
- private Dictionary<PacketType, List<PacketDelegate>> incomingDelegates = new Dictionary<PacketType, List<PacketDelegate>>();
- private Dictionary<PacketType, List<PacketDelegate>> outgoingDelegates = new Dictionary<PacketType, List<PacketDelegate>>();
- private List<Packet> queuedIncomingInjections = new List<Packet>();
- private List<Packet> queuedOutgoingInjections = new List<Packet>();
- // InitializeSimProxy: initialize the sim proxy
- private void InitializeSimProxy()
- {
- InitializeAddressCheckers();
- simFacingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- simFacingSocket.Bind(new IPEndPoint(proxyConfig.remoteFacingAddress, 0));
- Reset();
- }
- // Reset: start a new session
- private void Reset()
- {
- foreach (SimProxy simProxy in simProxies.Values)
- simProxy.Reset();
- KnownCaps = new Dictionary<string, CapInfo>();
- }
- private byte[] receiveBuffer = new byte[8192];
- private byte[] zeroBuffer = new byte[8192];
- private EndPoint remoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
- // RunSimProxy: start listening for packets from remote sims
- private void RunSimProxy()
- {
- #if DEBUG_THREADS
- Console.WriteLine(">R> RunSimProxy");
- #endif
- simFacingSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, new AsyncCallback(ReceiveFromSim), null);
- }
- // ReceiveFromSim: packet received from a remote sim
- private void ReceiveFromSim(IAsyncResult ar)
- {
- lock (this) try
- {
- // pause listening and fetch the packet
- bool needsZero = false;
- bool needsCopy = true;
- int length;
- length = simFacingSocket.EndReceiveFrom(ar, ref remoteEndPoint);
- if (proxyHandlers.ContainsKey(remoteEndPoint))
- {
- // find the proxy responsible for forwarding this packet
- SimProxy simProxy = (SimProxy)proxyHandlers[remoteEndPoint];
- // interpret the packet according to the SL protocol
- Packet packet;
- int end = length - 1;
- packet = Packet.BuildPacket(receiveBuffer, ref end, zeroBuffer);
- #if DEBUG_SEQUENCE
- Console.WriteLine("<- " + packet.Type + " #" + packet.Header.Sequence);
- #endif
- // check for ACKs we're waiting for
- packet = simProxy.CheckAcks(packet, Direction.Incoming, ref length, ref needsCopy);
- // modify sequence numbers to account for injections
- uint oldSequence = packet.Header.Sequence;
- packet = simProxy.ModifySequence(packet, Direction.Incoming, ref length, ref needsCopy);
- // keep track of sequence numbers
- if (packet.Header.Sequence > simProxy.incomingSequence)
- simProxy.incomingSequence = packet.Header.Sequence;
- // check the packet for addresses that need proxying
- if (incomingCheckers.ContainsKey(packet.Type))
- {
- /* if (needsZero) {
- length = Helpers.ZeroDecode(packet.Header.Data, length, zeroBuffer);
- packet.Header.Data = zeroBuffer;
- needsZero = false;
- } */
- Packet newPacket = ((AddressChecker)incomingCheckers[packet.Type])(packet);
- SwapPacket(packet, newPacket);
- packet = newPacket;
- needsCopy = false;
- }
- // pass the packet to any callback delegates
- if (incomingDelegates.ContainsKey(packet.Type))
- {
- /* if (needsZero) {
- length = Helpers.ZeroDecode(packet.Header.Data, length, zeroBuffer);
- packet.Header.Data = zeroBuffer;
- needsCopy = true;
- } */
- if (needsCopy)
- {
- byte[] newData = new byte[packet.Header.Data.Length];
- Array.Copy(packet.Header.Data, 0, newData, 0, packet.Header.Data.Length);
- packet.Header.Data = newData; // FIXME
- }
- try
- {
- Packet newPacket = callDelegates(incomingDelegates, packet, (IPEndPoint)remoteEndPoint);
- if (newPacket == null)
- {
- if ((packet.Header.Flags & Helpers.MSG_RELIABLE) != 0)
- simProxy.Inject(SpoofAck(oldSequence), Direction.Outgoing);
- if ((packet.Header.Flags & Helpers.MSG_APPENDED_ACKS) != 0)
- packet = SeparateAck(packet);
- else
- packet = null;
- }
- else
- {
- bool oldReliable = (packet.Header.Flags & Helpers.MSG_RELIABLE) != 0;
- bool newReliable = (newPacket.Header.Flags & Helpers.MSG_RELIABLE) != 0;
- if (oldReliable && !newReliable)
- simProxy.Inject(SpoofAck(oldSequence), Direction.Outgoing);
- else if (!oldReliable && newReliable)
- simProxy.WaitForAck(packet, Direction.Incoming);
- SwapPacket(packet, newPacket);
- packet = newPacket;
- }
- }
- catch (Exception e)
- {
- Log("exception in incoming delegate: " + e.Message, true);
- Log(e.StackTrace, true);
- }
- if (packet != null)
- simProxy.SendPacket(packet, false);
- }
- else
- simProxy.SendPacket(packet, needsZero);
- }
- else
- // ignore packets from unknown peers
- Log("dropping packet from " + remoteEndPoint, false);
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- Console.WriteLine(e.StackTrace);
- }
- finally
- {
- // resume listening
- simFacingSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, new AsyncCallback(ReceiveFromSim), null);
- }
- }
- // SendPacket: send a packet to a sim from our fake client endpoint
- public void SendPacket(Packet packet, IPEndPoint endPoint, bool skipZero)
- {
- byte[] buffer = packet.ToBytes();
- if (skipZero || (packet.Header.Data[0] & Helpers.MSG_ZEROCODED) == 0)
- simFacingSocket.SendTo(buffer, buffer.Length, SocketFlags.None, endPoint);
- else
- {
- int zeroLength = Helpers.ZeroEncode(buffer, buffer.Length, zeroBuffer);
- simFacingSocket.SendTo(zeroBuffer, zeroLength, SocketFlags.None, endPoint);
- }
- }
- // SpoofAck: create an ACK for the given packet
- public Packet SpoofAck(uint sequence)
- {
- PacketAckPacket spoof = new PacketAckPacket();
- spoof.Packets = new PacketAckPacket.PacketsBlock[1];
- spoof.Packets[0] = new PacketAckPacket.PacketsBlock();
- spoof.Packets[0].ID = sequence;
- return (Packet)spoof;
- //Legacy:
- ////Hashtable blocks = new Hashtable();
- ////Hashtable fields = new Hashtable();
- ////fields["ID"] = (uint)sequence;
- ////blocks[fields] = "Packets";
- ////return .BuildPacket("PacketAck", proxyConfig.protocol, blocks, Helpers.MSG_ZEROCODED);
- }
- // SeparateAck: create a standalone PacketAck for packet's appended ACKs
- public Packet SeparateAck(Packet packet)
- {
- PacketAckPacket seperate = new PacketAckPacket();
- seperate.Packets = new PacketAckPacket.PacketsBlock[packet.Header.AckList.Length];
- for (int i = 0; i < packet.Header.AckList.Length; ++i)
- {
- seperate.Packets[i].ID = packet.Header.AckList[i];
- }
- Packet ack = seperate;
- ack.Header.Sequence = packet.Header.Sequence;
- return ack;
- }
- // SwapPacket: copy the sequence number and appended ACKs from one packet to another
- public static void SwapPacket(Packet oldPacket, Packet newPacket)
- {
- newPacket.Header.Sequence = oldPacket.Header.Sequence;
- int oldAcks = (oldPacket.Header.Data[0] & Helpers.MSG_APPENDED_ACKS) == 0 ? 0 : oldPacket.Header.AckList.Length;
- int newAcks = (newPacket.Header.Data[0] & Helpers.MSG_APPENDED_ACKS) == 0 ? 0 : newPacket.Header.AckList.Length;
- if (oldAcks != 0 || newAcks != 0)
- {
- uint[] newAckList = new uint[oldAcks];
- Array.Copy(oldPacket.Header.AckList, 0, newAckList, 0, oldAcks);
- newPacket.Header.AckList = newAckList;
- newPacket.Header.AppendedAcks = oldPacket.Header.AppendedAcks;
- }
- }
- // ProxySim: return the proxy for the s…
Large files files are truncated, but you can click here to view the full file