2007-04-24 07:08:18 +00:00
/ *
2008-07-23 15:35:39 +00:00
* GridProxy . cs : implementation of OpenMetaverse proxy library
2007-04-24 07:08:18 +00:00
*
* 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 .
2015-11-06 19:00:05 +01:00
* - Neither the name of the openmetaverse . co nor the names
2007-04-24 07:08:18 +00:00
* 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 .
2007-04-28 20:50:45 +00:00
* /
using System ;
using System.IO ;
using System.Net ;
2009-10-22 06:31:49 +00:00
using System.Xml ;
2007-04-28 20:50:45 +00:00
using System.Text ;
using System.Threading ;
2009-10-22 06:31:49 +00:00
using System.Net.Sockets ;
using System.Collections.Generic ;
2019-12-04 17:26:35 -06:00
using System.Net.Http ;
2009-10-22 06:31:49 +00:00
using System.Text.RegularExpressions ;
2008-07-21 21:12:59 +00:00
using OpenMetaverse ;
2008-12-29 20:44:28 +00:00
using OpenMetaverse.Http ;
2008-07-21 21:12:59 +00:00
using OpenMetaverse.Packets ;
2009-10-22 06:31:49 +00:00
using OpenMetaverse.StructuredData ;
2019-11-11 17:33:28 -06:00
using XmlRpcCore ;
2007-04-28 20:50:45 +00:00
2008-07-23 15:35:39 +00:00
namespace GridProxy
2007-04-28 20:50:45 +00:00
{
2009-07-10 00:24:52 +00:00
/// <summary>
/// Proxy Configuration Class
/// </summary>
2007-04-28 20:50:45 +00:00
public class ProxyConfig
{
2009-07-10 00:24:52 +00:00
/// <summary>
/// The user agent reported to the remote server
/// </summary>
2007-04-28 20:50:45 +00:00
public string userAgent ;
2009-07-10 00:24:52 +00:00
/// <summary>
/// Email address of the proxy application's author
/// </summary>
2007-04-28 20:50:45 +00:00
public string author ;
2009-07-10 00:24:52 +00:00
/// <summary>
/// The port the proxy server will listen on
/// </summary>
2007-04-28 20:50:45 +00:00
public ushort loginPort = 8080 ;
2009-07-10 00:24:52 +00:00
/// <summary>
/// The IP Address the proxy server will communication with the client on
/// </summary>
2007-04-28 20:50:45 +00:00
public IPAddress clientFacingAddress = IPAddress . Loopback ;
2009-07-10 00:24:52 +00:00
/// <summary>
/// The IP Address the proxy server will communicate with the server on
/// </summary>
2007-04-28 20:50:45 +00:00
public IPAddress remoteFacingAddress = IPAddress . Any ;
2009-07-10 00:24:52 +00:00
/// <summary>
/// The URI of the login server
/// </summary>
2007-04-28 20:50:45 +00:00
public Uri remoteLoginUri = new Uri ( "https://login.agni.lindenlab.com/cgi-bin/login.cgi" ) ;
2010-08-01 00:32:11 +00:00
2009-07-10 00:24:52 +00:00
/// <summary>
/// construct a default proxy configuration with the specified userAgent and author
/// </summary>
/// <param name="userAgent">The user agent reported to the remote server</param>
/// <param name="author">Email address of the proxy application's author</param>
2007-04-28 20:50:45 +00:00
public ProxyConfig ( string userAgent , string author )
{
this . userAgent = userAgent ;
this . author = author ;
}
2009-07-10 00:24:52 +00:00
/// <summary>
/// construct a default proxy configuration, parsing command line arguments (try --help)
/// </summary>
/// <param name="userAgent">The user agent reported to the remote server</param>
/// <param name="author">Email address of the proxy application's author</param>
/// <param name="args">An array containing the parameters to use to override the proxy
/// servers default settings</param>
2024-06-30 18:14:07 -05:00
/// <param name="exitOnError">Does what it says on the tin</param>
2013-12-21 19:08:26 +01:00
public ProxyConfig ( string userAgent , string author , string [ ] args , bool exitOnError )
2007-04-28 20:50:45 +00:00
: this ( userAgent , author )
{
2007-11-06 09:26:10 +00:00
Dictionary < string , ArgumentParser > argumentParsers = new Dictionary < string , ArgumentParser > ( ) ;
2008-08-27 20:30:30 +00:00
argumentParsers [ "help" ] = new ArgumentParser ( ParseHelp ) ;
2007-04-28 20:50:45 +00:00
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 ) ;
foreach ( string arg in args )
2009-10-22 06:31:49 +00:00
{
2007-04-28 20:50:45 +00:00
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 ) ;
2013-12-21 19:08:26 +01:00
if ( exitOnError )
{
ParseHelp ( null ) ;
}
else
{
throw ;
}
2007-04-28 20:50:45 +00:00
}
}
}
2009-10-22 06:31:49 +00:00
}
2007-04-28 20:50:45 +00:00
}
private delegate void ArgumentParser ( string value ) ;
private void ParseHelp ( string value )
{
Console . WriteLine ( "Proxy command-line arguments:" ) ;
2008-08-27 20:30:30 +00:00
Console . WriteLine ( " --help display this help" ) ;
2007-04-28 20:50:45 +00:00
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>" ) ;
2008-08-27 20:30:30 +00:00
Console . WriteLine ( " --log-all log all packets by default in Analyst" ) ;
2009-04-17 21:06:51 +00:00
Console . WriteLine ( " --log-whitelist=<file> log packets listed in file, one name per line" ) ;
Console . WriteLine ( " --no-log-blacklist=<file> don't log packets in file, one name per line" ) ;
2008-08-27 20:30:30 +00:00
Console . WriteLine ( " --output=<logfile> log Analyst output to a file" ) ;
2007-04-28 20:50:45 +00:00
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 ) ;
2010-08-01 00:32:11 +00:00
}
2007-04-28 20:50:45 +00:00
}
2008-07-21 21:12:59 +00:00
// Proxy: OpenMetaverse proxy server
2007-04-28 20:50:45 +00:00
// A Proxy instance is only prepared to deal with one client at a time.
public class Proxy
{
2009-10-25 09:47:25 +00:00
public ProxyConfig proxyConfig ;
2007-04-28 20:50:45 +00:00
private string loginURI ;
2019-12-04 17:26:35 -06:00
private static readonly HttpClient HttpClient = new HttpClient ( ) ;
2010-09-03 16:13:35 +00:00
static List < string > BinaryResponseCaps = new List < string > ( )
{
"GetTexture" ,
2014-03-15 00:17:10 +01:00
"GetMesh" ,
"GetMesh2"
2010-09-03 16:13:35 +00:00
} ;
2007-04-28 20:50:45 +00:00
2007-04-24 07:08:18 +00:00
/ *
* Proxy Management
2007-04-28 20:50:45 +00:00
* /
// Proxy: construct a proxy server with the given configuration
public Proxy ( ProxyConfig proxyConfig )
{
this . proxyConfig = proxyConfig ;
2017-03-24 00:22:15 -04:00
ServicePointManager . ServerCertificateValidationCallback = ( sender , certificate , chain , sslPolicyErrors ) = > true ;
2011-04-22 02:56:55 +00:00
ServicePointManager . Expect100Continue = false ;
2009-08-03 22:27:00 +00:00
2007-04-28 20:50:45 +00:00
InitializeLoginProxy ( ) ;
InitializeSimProxy ( ) ;
InitializeCaps ( ) ;
}
2009-11-12 00:15:59 +00:00
object keepAliveLock = new object ( ) ;
2007-04-28 20:50:45 +00:00
// Start: begin accepting clients
public void Start ( )
{
lock ( this )
{
System . Threading . Monitor . Enter ( keepAliveLock ) ;
( new Thread ( new ThreadStart ( KeepAlive ) ) ) . Start ( ) ;
RunSimProxy ( ) ;
2009-11-12 00:15:59 +00:00
2020-05-09 12:51:59 -05:00
Thread runLoginProxy = new Thread ( new ThreadStart ( RunLoginProxy ) )
{
IsBackground = true , Name = "Login Proxy"
} ;
2007-04-28 20:50:45 +00:00
runLoginProxy . Start ( ) ;
IPEndPoint endPoint = ( IPEndPoint ) loginServer . LocalEndPoint ;
2020-05-09 12:51:59 -05:00
var displayAddress = Equals ( endPoint . Address , IPAddress . Any )
? IPAddress . Loopback : endPoint . Address ;
2007-04-28 20:50:45 +00:00
loginURI = "http://" + displayAddress + ":" + endPoint . Port + "/" ;
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Proxy ready at " + loginURI , Helpers . LogLevel . Info ) ;
2007-04-28 20:50:45 +00:00
}
}
// 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 ( )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( ">T> KeepAlive" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
lock ( keepAliveLock ) { } ;
2009-04-23 10:02:39 +00:00
if ( loginServer . Connected )
{
loginServer . Disconnect ( false ) ;
loginServer . Shutdown ( SocketShutdown . Both ) ;
}
loginServer . Close ( ) ;
2010-08-01 00:32:11 +00:00
OpenMetaverse . Logger . Log ( "<T< KeepAlive" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
}
// 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 ] ;
2024-06-30 17:23:29 -05:00
delegateArray . Remove ( packetDelegate ) ;
2007-04-28 20:50:45 +00:00
}
}
2024-07-01 13:25:54 -05:00
private Packet callDelegates ( Dictionary < PacketType , List < PacketDelegate > > delegates , Packet packet , IPEndPoint endpoint )
2007-04-28 20:50:45 +00:00
{
PacketType origType = packet . Type ;
foreach ( PacketDelegate del in delegates [ origType ] )
{
2024-07-01 13:25:54 -05:00
try { packet = del ( packet , endpoint ) ; }
2009-07-10 00:24:52 +00:00
catch ( Exception ex ) { OpenMetaverse . Logger . Log ( "Error in packet delegate" , Helpers . LogLevel . Warning , ex ) ; }
2007-04-28 20:50:45 +00:00
// 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
2007-11-06 09:26:10 +00:00
List < Packet > queue = direction = = Direction . Incoming ? queuedIncomingInjections : queuedOutgoingInjections ;
2007-04-28 20:50:45 +00:00
queue . Add ( packet ) ;
}
else
// tell the active sim proxy to inject the packet
( ( SimProxy ) simProxies [ activeCircuit ] ) . Inject ( packet , direction ) ;
}
}
2007-04-24 07:08:18 +00:00
/ *
* Login Proxy
2007-04-28 20:50:45 +00:00
* /
private Socket loginServer ;
private int capsReqCount = 0 ;
// InitializeLoginProxy: initialize the login proxy
private void InitializeLoginProxy ( )
{
2009-04-22 00:01:30 +00:00
try
{
loginServer = new Socket ( AddressFamily . InterNetwork , SocketType . Stream , ProtocolType . Tcp ) ;
loginServer . Bind ( new IPEndPoint ( proxyConfig . clientFacingAddress , proxyConfig . loginPort ) ) ;
loginServer . Listen ( 1 ) ;
}
catch ( SocketException e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Socket Exception" , Helpers . LogLevel . Error , e ) ;
2009-04-23 10:02:39 +00:00
}
catch ( ObjectDisposedException e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Socket Object is disposed Exception" , Helpers . LogLevel . Error , e ) ;
2009-04-22 00:01:30 +00:00
}
2007-04-28 20:50:45 +00:00
}
// RunLoginProxy: process login requests from clients
private void RunLoginProxy ( )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( ">T> RunLoginProxy" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
try
{
for ( ; ; )
{
try
{
2009-07-25 10:56:03 +00:00
Socket client = loginServer . Accept ( ) ;
2007-04-28 20:50:45 +00:00
Thread connThread = new Thread ( ( ThreadStart ) delegate
{
2009-10-22 06:31:49 +00:00
OpenMetaverse . Logger . Log ( ">T> LoginProxy" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
ProxyHTTP ( client ) ;
2009-10-22 06:31:49 +00:00
OpenMetaverse . Logger . Log ( "<T< LoginProxy" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
} ) ;
2009-05-25 19:00:28 +00:00
2007-04-28 20:50:45 +00:00
connThread . IsBackground = true ;
2009-10-22 06:31:49 +00:00
connThread . Name = "LoginProxy" ;
2007-04-28 20:50:45 +00:00
connThread . Start ( ) ;
}
2009-10-22 05:42:24 +00:00
catch ( SocketException e )
2007-04-28 20:50:45 +00:00
{
2009-10-22 05:42:24 +00:00
// indicates we've told the listener to shutdown
if ( e . SocketErrorCode = = SocketError . Interrupted )
break ;
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Login Failed" , Helpers . LogLevel . Error , e ) ;
2009-10-22 05:42:24 +00:00
break ;
}
2009-10-26 06:03:26 +00:00
catch ( ObjectDisposedException )
2009-10-22 05:42:24 +00:00
{
break ;
2007-04-28 20:50:45 +00:00
}
// send any packets queued for injection
2009-10-22 06:31:49 +00:00
if ( activeCircuit ! = null )
{
lock ( this )
2007-04-28 20:50:45 +00:00
{
SimProxy activeProxy = ( SimProxy ) simProxies [ activeCircuit ] ;
foreach ( Packet packet in queuedOutgoingInjections )
activeProxy . Inject ( packet , Direction . Outgoing ) ;
2007-11-06 09:26:10 +00:00
queuedOutgoingInjections = new List < Packet > ( ) ;
2007-04-28 20:50:45 +00:00
}
2009-10-22 06:31:49 +00:00
}
2007-04-28 20:50:45 +00:00
}
}
catch ( Exception e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Exception in RunLoginProxy" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "<T< RunLoginProxy" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
}
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 ;
}
2011-04-22 22:45:06 +00:00
if ( bufFill < ( i + 1 ) ) return null ;
2007-04-28 20:50:45 +00:00
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 ( )
{
2010-05-03 05:42:12 +00:00
try
{
int n = netStream . Read ( buf , bufFill , BUF_SIZE - bufFill ) ;
bufFill + = n ;
return n > 0 ;
}
catch
{
return false ;
}
2007-04-28 20:50:45 +00:00
}
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 ;
}
2011-04-22 22:45:06 +00:00
if ( bufFill < len ) return 0 ;
2007-04-28 20:50:45 +00:00
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 ) ;
2007-08-10 20:16:19 +00:00
string line = null ;
int reqNo ;
2007-04-28 20:50:45 +00:00
int contentLength = 0 ;
2008-04-11 01:20:50 +00:00
string contentType = "" ;
2007-04-28 20:50:45 +00:00
Dictionary < string , string > headers = new Dictionary < string , string > ( ) ;
lock ( this )
{
capsReqCount + + ; reqNo = capsReqCount ;
}
2007-08-10 20:16:19 +00:00
byte [ ] byteLine = reader . ReadLine ( ) ;
2010-08-01 00:32:11 +00:00
if ( byteLine = = null )
2009-03-18 21:41:34 +00:00
{
//This dirty hack is part of the LIBOMV-457 workaround
//The connecting libomv client being proxied can manage to trigger a null from the ReadLine()
//The happens just after the seed request and is not seen again. TODO find this bug in the library.
netStream . Close ( ) ; client . Close ( ) ;
return ;
}
2007-08-10 20:16:19 +00:00
if ( byteLine ! = null ) line = Encoding . UTF8 . GetString ( byteLine ) . Replace ( "\r" , "" ) ;
2007-04-28 20:50:45 +00:00
if ( line = = null )
throw new Exception ( "EOF in client HTTP header" ) ;
2024-06-30 18:10:23 -05:00
var match = new Regex ( @"^(\S+)\s+(\S+)\s+(HTTP/\d\.\d)$" ) . Match ( line ) ;
2007-04-28 20:50:45 +00:00
if ( ! match . Success )
{
2009-07-10 00:24:52 +00:00
OpenMetaverse . Logger . Log ( "[" + reqNo + "] Bad request!" , Helpers . LogLevel . Warning ) ;
2007-04-28 20:50:45 +00:00
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 ;
}
2024-06-30 18:10:23 -05:00
var meth = match . Groups [ 1 ] . Captures [ 0 ] . ToString ( ) ;
var uri = match . Groups [ 2 ] . Captures [ 0 ] . ToString ( ) ;
2007-04-28 20:50:45 +00:00
2024-06-30 16:48:27 -05:00
OpenMetaverse . Logger . Log ( $"[{reqNo}] {meth}:{uri}" , Helpers . LogLevel . Debug ) ;
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
// 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 )
{
2024-06-30 16:48:27 -05:00
OpenMetaverse . Logger . Log ( $"[{reqNo}] Bad Header: '{line}'" , Helpers . LogLevel . Warning ) ;
2007-04-28 20:50:45 +00:00
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" ] ) ;
}
2010-08-01 00:32:11 +00:00
if ( headers . ContainsKey ( "content-type" ) )
{
2008-04-11 01:20:50 +00:00
contentType = headers [ "content-type" ] ;
}
2007-04-28 20:50:45 +00:00
// read the HTTP body into a buffer
byte [ ] content = new byte [ contentLength ] ;
reader . Read ( content , 0 , contentLength ) ;
2010-08-01 00:32:11 +00:00
if ( contentLength < 8192 )
2024-06-30 16:48:27 -05:00
OpenMetaverse . Logger . Log ( $"[{reqNo}] request length={contentLength}:\n{Utils.BytesToString(content)}" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
if ( uri = = "/" )
{
2010-08-01 00:32:11 +00:00
if ( contentType = = "application/xml+llsd" | | contentType = = "application/xml" )
{
2008-10-30 01:50:59 +00:00
ProxyLoginSD ( netStream , content ) ;
2010-08-01 00:32:11 +00:00
}
else
{
2008-04-11 01:20:50 +00:00
ProxyLogin ( netStream , content ) ;
}
2007-04-28 20:50:45 +00:00
}
2024-06-30 16:38:00 -05:00
else if ( new Regex ( "^/https?://.*$" ) . Match ( uri ) . Success )
2007-04-28 20:50:45 +00:00
{
ProxyCaps ( netStream , meth , uri . Substring ( 1 ) , headers , content , reqNo ) ;
}
2024-06-30 16:38:00 -05:00
else if ( new Regex ( "^/https?:/.*$" ) . Match ( uri ) . Success )
2009-03-18 21:41:34 +00:00
{
//This is a libomv client and the proxy CAPS URI has been munged by the C# URI class
//Part of the LIBOMV-457 work around, TODO make this much nicer.
2010-08-01 00:32:11 +00:00
uri = uri . Replace ( ":/" , "://" ) ;
2009-03-18 21:41:34 +00:00
ProxyCaps ( netStream , meth , uri . Substring ( 1 ) , headers , content , reqNo ) ;
}
2007-04-28 20:50:45 +00:00
else
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "404 not found: " + uri , Helpers . LogLevel . Error ) ;
2007-04-28 20:50:45 +00:00
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 ( ) ;
}
2010-08-01 00:32:11 +00:00
public ObservableDictionary < string , CapInfo > KnownCaps = new ObservableDictionary < string , CapInfo > ( ) ;
2008-10-09 16:48:44 +00:00
//private Dictionary<string, bool> SubHack = new Dictionary<string, bool>();
2007-04-28 20:50:45 +00:00
private void ProxyCaps ( NetworkStream netStream , string meth , string uri , Dictionary < string , string > headers , byte [ ] content , int reqNo )
2010-08-01 00:32:11 +00:00
{
2007-04-28 20:50:45 +00:00
Match match = new Regex ( @"^(https?)://([^:/]+)(:\d+)?(/.*)$" ) . Match ( uri ) ;
if ( ! match . Success )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "[" + reqNo + "] Malformed proxy URI: " + uri , Helpers . LogLevel . Error ) ;
2007-04-28 20:50:45 +00:00
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 ;
}
CapInfo cap = null ;
lock ( this )
{
2014-03-15 00:17:10 +01:00
string capuri = Regex . Replace ( uri , @"/?\?.*$" , string . Empty ) ;
2010-09-03 16:13:35 +00:00
if ( KnownCaps . ContainsKey ( capuri ) )
2007-04-28 20:50:45 +00:00
{
2010-09-03 16:13:35 +00:00
cap = KnownCaps [ capuri ] ;
2007-04-28 20:50:45 +00:00
}
}
CapsRequest capReq = null ; bool shortCircuit = false ; bool requestFailed = false ;
if ( cap ! = null )
2010-08-01 00:32:11 +00:00
{
2007-04-28 20:50:45 +00:00
capReq = new CapsRequest ( cap ) ;
2007-11-06 09:26:10 +00:00
2009-07-10 00:24:52 +00:00
if ( cap . ReqFmt = = CapsDataFormat . OSD )
2007-04-28 20:50:45 +00:00
{
2008-10-30 16:54:17 +00:00
capReq . Request = OSDParser . DeserializeLLSDXml ( content ) ;
2007-04-28 20:50:45 +00:00
}
else
{
2008-10-30 16:54:17 +00:00
capReq . Request = OSDParser . DeserializeLLSDXml ( content ) ;
2007-04-28 20:50:45 +00:00
}
2009-04-22 00:01:30 +00:00
capReq . RawRequest = content ;
2010-09-03 16:13:35 +00:00
capReq . FullUri = uri ;
2009-04-22 00:01:30 +00:00
2007-04-28 20:50:45 +00:00
foreach ( CapsDelegate d in cap . GetDelegates ( ) )
{
if ( d ( capReq , CapsStage . Request ) ) { shortCircuit = true ; break ; }
}
}
2007-11-21 23:50:05 +00:00
byte [ ] respBuf = null ;
2024-06-30 17:47:14 -05:00
string consoleMsg = string . Empty ;
2007-04-28 20:50:45 +00:00
if ( shortCircuit )
{
byte [ ] wr = Encoding . UTF8 . GetBytes ( "HTTP/1.0 200 OK\r\n" ) ;
netStream . Write ( wr , 0 , wr . Length ) ;
}
else
{
2022-10-10 16:13:37 -05:00
HttpWebRequest req = ( HttpWebRequest ) WebRequest . Create ( uri ) ;
2007-06-03 18:36:07 +00:00
req . KeepAlive = false ;
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
foreach ( string header in headers . Keys )
{
2010-05-25 19:48:37 +00:00
if ( header = = "connection" | |
2007-04-28 20:50:45 +00:00
header = = "content-length" | | header = = "date" | | header = = "expect" | |
header = = "host" | | header = = "if-modified-since" | | header = = "referer" | |
header = = "transfer-encoding" | | header = = "user-agent" | |
2011-04-22 02:56:55 +00:00
header = = "proxy-connection" | | header = = "accept-encoding" )
2007-04-28 20:50:45 +00:00
{
// can't touch these!
}
2010-05-25 19:48:37 +00:00
else if ( header = = "accept" )
{
req . Accept = headers [ "accept" ] ;
}
2007-04-28 20:50:45 +00:00
else if ( header = = "content-type" )
{
req . ContentType = headers [ "content-type" ] ;
}
2010-05-03 05:42:12 +00:00
else if ( header = = "range" )
{
string rangeHeader = headers [ header ] ;
string [ ] parts = rangeHeader . Split ( '=' ) ;
2010-08-01 00:32:11 +00:00
2010-05-03 05:42:12 +00:00
if ( parts . Length = = 2 )
{
string [ ] range = parts [ 1 ] . Split ( '-' ) ;
2010-08-01 00:32:11 +00:00
int from ;
int to ;
2010-05-03 05:42:12 +00:00
if ( range . Length = = 2 )
{
if ( int . TryParse ( range [ 0 ] , out from )
& & int . TryParse ( range [ 1 ] , out to ) )
{
2010-08-01 00:32:11 +00:00
req . AddRange ( parts [ 0 ] , from , to ) ;
2010-05-03 05:42:12 +00:00
}
}
2010-08-01 00:32:11 +00:00
else if ( range . Length = = 1 & & int . TryParse ( range [ 0 ] , out to ) )
{
req . AddRange ( parts [ 0 ] , to ) ;
}
2010-05-03 05:42:12 +00:00
}
}
2007-04-28 20:50:45 +00:00
else
{
req . Headers [ header ] = headers [ header ] ;
}
}
2010-05-25 19:48:37 +00:00
if ( capReq ! = null )
2009-11-03 05:32:49 +00:00
{
2010-05-25 19:48:37 +00:00
capReq . RequestHeaders = req . Headers ;
2009-11-03 05:32:49 +00:00
}
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
req . Method = meth ;
2010-08-01 00:32:11 +00:00
2009-05-09 07:57:57 +00:00
// can't do gets on requests with a content body
// without throwing a protocol exception. So force it to post
// incase our parser stupidly set it to GET due to the viewer
// doing something stupid like sending an empty request
2010-08-01 00:32:11 +00:00
if ( content . Length > 0 )
2009-05-09 07:57:57 +00:00
req . Method = "POST" ;
2007-04-28 20:50:45 +00:00
req . ContentLength = content . Length ;
HttpWebResponse resp ;
try
{
2010-05-25 19:48:37 +00:00
if ( content . Length > 0 )
{
Stream reqStream = req . GetRequestStream ( ) ;
reqStream . Write ( content , 0 , content . Length ) ;
reqStream . Close ( ) ;
}
2010-09-03 16:13:35 +00:00
else if ( cap = = null )
2010-05-25 19:48:37 +00:00
{
2024-06-30 16:48:27 -05:00
OpenMetaverse . Logger . Log ( $"{req.Method} {req.Address}" , Helpers . LogLevel . Info ) ;
2010-05-25 19:48:37 +00:00
}
2007-04-28 20:50:45 +00:00
resp = ( HttpWebResponse ) req . GetResponse ( ) ;
}
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
catch ( WebException e )
{
if ( e . Status = = WebExceptionStatus . Timeout | | e . Status = = WebExceptionStatus . SendFailure )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Request timeout" , Helpers . LogLevel . Warning , e ) ;
2007-04-28 20:50:45 +00:00
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
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Request error" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
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 )
{
2009-07-10 00:24:52 +00:00
if ( cap . RespFmt = = CapsDataFormat . OSD )
2007-04-28 20:50:45 +00:00
{
2008-10-30 16:54:17 +00:00
capReq . Response = OSDParser . DeserializeLLSDXml ( respBuf ) ;
2007-04-28 20:50:45 +00:00
}
else
{
2008-10-30 16:54:17 +00:00
capReq . Response = OSDParser . DeserializeLLSDXml ( respBuf ) ;
2007-04-28 20:50:45 +00:00
}
2009-04-22 00:01:30 +00:00
capReq . RawResponse = respBuf ;
2007-04-28 20:50:45 +00:00
}
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 ) ;
}
2010-08-01 00:32:11 +00:00
if ( capReq ! = null )
capReq . ResponseHeaders = resp . Headers ;
2007-04-28 20:50:45 +00:00
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 ) ;
}
}
}
2010-08-01 00:32:11 +00:00
catch ( Exception ex )
2007-04-28 20:50:45 +00:00
{
// TODO: Should we handle this somehow?
2010-08-01 00:32:11 +00:00
OpenMetaverse . Logger . DebugLog ( "Failed writing output: " + ex . Message ) ;
2007-04-28 20:50:45 +00:00
}
}
2009-11-12 00:15:59 +00:00
if ( cap ! = null & & ! requestFailed & & ! capReq . Response . ToString ( ) . Equals ( "undef" ) )
2007-04-28 20:50:45 +00:00
{
foreach ( CapsDelegate d in cap . GetDelegates ( ) )
{
2007-11-06 09:26:10 +00:00
try
{
if ( d ( capReq , CapsStage . Response ) ) { break ; }
}
2009-11-12 00:15:59 +00:00
catch ( InvalidCastException ex )
{
2024-06-30 16:35:53 -05:00
OpenMetaverse . Logger . Log ( "Invalid Cast thrown trying to cast OSD to OSDMap: \n'" + capReq . Response . AsString ( ) + "' Length=" + capReq . RawResponse . Length + "\n" ,
2009-11-12 00:15:59 +00:00
Helpers . LogLevel . Error , ex ) ;
}
catch ( Exception ex )
2007-11-06 09:26:10 +00:00
{
2009-11-12 00:15:59 +00:00
OpenMetaverse . Logger . Log ( "Error firing delegate" , Helpers . LogLevel . Error , ex ) ;
2007-11-06 09:26:10 +00:00
}
2007-04-28 20:50:45 +00:00
}
2009-07-10 00:24:52 +00:00
if ( cap . RespFmt = = CapsDataFormat . OSD )
2007-04-28 20:50:45 +00:00
{
2008-10-30 16:54:17 +00:00
respBuf = OSDParser . SerializeLLSDXmlBytes ( ( OSD ) capReq . Response ) ;
2007-04-28 20:50:45 +00:00
}
else
{
2008-10-30 16:54:17 +00:00
respBuf = OSDParser . SerializeLLSDXmlBytes ( capReq . Response ) ;
2007-04-28 20:50:45 +00:00
}
}
2010-08-01 01:26:18 +00:00
string respString ;
2010-09-03 16:13:35 +00:00
if ( cap = = null | | cap . RespFmt = = CapsDataFormat . Binary )
2010-08-01 01:26:18 +00:00
{
respString = "<data>" ;
}
else
{
respString = Encoding . UTF8 . GetString ( respBuf ) ;
}
consoleMsg + = "\n" + respString + "\n--------" ;
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( consoleMsg , Helpers . LogLevel . Debug ) ;
2010-08-01 01:26:18 +00:00
OpenMetaverse . Logger . Log ( "[" + reqNo + "] Fixed-up response:\n" + respString + "\n--------" , Helpers . LogLevel . Debug ) ;
2007-04-28 20:50:45 +00:00
2007-11-06 09:26:10 +00:00
try
{
byte [ ] wr2 = Encoding . UTF8 . GetBytes ( "Content-Length: " + respBuf . Length + "\r\n\r\n" ) ;
netStream . Write ( wr2 , 0 , wr2 . Length ) ;
2007-04-28 20:50:45 +00:00
2007-11-06 09:26:10 +00:00
netStream . Write ( respBuf , 0 , respBuf . Length ) ;
}
2010-08-01 00:32:11 +00:00
catch ( SocketException ) { }
catch ( IOException ) { }
2007-11-06 09:26:10 +00:00
catch ( Exception e )
2007-04-28 20:50:45 +00:00
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Exception: Error writing to stream " + e , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
return ;
}
private bool FixupSeedCapsResponse ( CapsRequest capReq , CapsStage stage )
{
if ( stage ! = CapsStage . Response ) return false ;
2008-10-30 01:50:59 +00:00
OSDMap nm = new OSDMap ( ) ;
2008-01-05 05:16:39 +00:00
2008-10-30 01:50:59 +00:00
if ( capReq . Response . Type = = OSDType . Map )
2007-04-28 20:50:45 +00:00
{
2008-10-30 01:50:59 +00:00
OSDMap m = ( OSDMap ) capReq . Response ;
2010-08-01 00:32:11 +00:00
2008-01-05 05:16:39 +00:00
foreach ( string key in m . Keys )
2007-04-28 20:50:45 +00:00
{
2008-01-05 05:16:39 +00:00
string val = m [ key ] . AsString ( ) ;
2024-06-30 17:47:14 -05:00
if ( ! string . IsNullOrEmpty ( val ) )
2007-04-28 20:50:45 +00:00
{
2008-01-05 05:16:39 +00:00
if ( ! KnownCaps . ContainsKey ( val ) )
{
2010-09-03 16:13:35 +00:00
CapsDataFormat resFmt = BinaryResponseCaps . Contains ( key ) ? CapsDataFormat . Binary : CapsDataFormat . OSD ;
2024-07-04 16:26:15 -05:00
const CapsDataFormat reqFmt = CapsDataFormat . OSD ;
2010-09-03 16:13:35 +00:00
CapInfo newCap = new CapInfo ( val , capReq . Info . Sim , key , reqFmt , resFmt ) ;
2008-01-05 05:16:39 +00:00
newCap . AddDelegate ( new CapsDelegate ( KnownCapDelegate ) ) ;
lock ( this ) { KnownCaps [ val ] = newCap ; }
}
2008-10-30 01:50:59 +00:00
nm [ key ] = OSD . FromString ( loginURI + val ) ;
2008-01-05 05:16:39 +00:00
}
else
{
2008-10-30 01:50:59 +00:00
nm [ key ] = OSD . FromString ( val ) ;
2007-04-28 20:50:45 +00:00
}
}
}
2008-01-05 05:16:39 +00:00
2007-04-28 20:50:45 +00:00
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 ] ;
2024-06-30 17:23:29 -05:00
delegateArray . Remove ( capsDelegate ) ;
2007-04-28 20:50:45 +00:00
}
}
private bool KnownCapDelegate ( CapsRequest capReq , CapsStage stage )
{
lock ( this )
{
if ( ! KnownCapsDelegates . ContainsKey ( capReq . Info . CapType ) )
return false ;
2009-07-10 00:24:52 +00:00
if ( stage = = CapsStage . Response )
{
2020-05-09 08:41:30 -05:00
if ( capReq . Response ! = null & & capReq . Response is OSDMap map )
2009-07-10 00:24:52 +00:00
{
2009-07-10 22:30:32 +00:00
if ( map . ContainsKey ( "uploader" ) )
{
string val = map [ "uploader" ] . AsString ( ) ;
2009-07-10 00:24:52 +00:00
2009-07-10 22:30:32 +00:00
if ( ! KnownCaps . ContainsKey ( val ) )
{
CapInfo newCap = new CapInfo ( val , capReq . Info . Sim , capReq . Info . CapType , CapsDataFormat . Binary , CapsDataFormat . OSD ) ;
newCap . AddDelegate ( new CapsDelegate ( KnownCapDelegate ) ) ;
lock ( this ) { KnownCaps [ val ] = newCap ; }
}
map [ "uploader" ] = OSD . FromString ( loginURI + val ) ;
}
2009-07-10 00:24:52 +00:00
}
}
2007-04-28 20:50:45 +00:00
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 ;
2012-03-31 00:17:26 +00:00
OSDMap map = null ;
2020-05-09 08:41:30 -05:00
if ( capReq . Response is OSDMap response )
map = response ;
2012-03-31 00:17:26 +00:00
else return false ;
OSDArray array = null ;
if ( map . ContainsKey ( "events" ) & & map [ "events" ] is OSDArray )
array = ( OSDArray ) map [ "events" ] ;
else
return false ;
2008-01-05 05:16:39 +00:00
for ( int i = 0 ; i < array . Count ; i + + )
2007-04-28 20:50:45 +00:00
{
2008-10-30 01:50:59 +00:00
OSDMap evt = ( OSDMap ) array [ i ] ;
2008-01-05 05:16:39 +00:00
string message = evt [ "message" ] . AsString ( ) ;
2008-10-30 01:50:59 +00:00
OSDMap body = ( OSDMap ) evt [ "body" ] ;
2008-01-05 05:16:39 +00:00
2007-04-28 20:50:45 +00:00
if ( message = = "TeleportFinish" | | message = = "CrossedRegion" )
{
2008-10-30 01:50:59 +00:00
OSDMap info = null ;
2007-04-28 20:50:45 +00:00
if ( message = = "TeleportFinish" )
2008-10-30 01:50:59 +00:00
info = ( OSDMap ) ( ( ( OSDArray ) body [ "Info" ] ) [ 0 ] ) ;
2007-04-28 20:50:45 +00:00
else
2008-10-30 01:50:59 +00:00
info = ( OSDMap ) ( ( ( OSDArray ) body [ "RegionData" ] ) [ 0 ] ) ;
2008-01-05 05:16:39 +00:00
byte [ ] bytes = info [ "SimIP" ] . AsBinary ( ) ;
2008-10-06 22:34:38 +00:00
uint simIP = Utils . BytesToUInt ( bytes ) ;
2008-01-05 05:16:39 +00:00
ushort simPort = ( ushort ) info [ "SimPort" ] . AsInteger ( ) ;
string capsURL = info [ "SeedCapability" ] . AsString ( ) ;
2020-05-09 12:51:59 -05:00
GenericCheck ( ref simIP , ref simPort , ref capsURL , Equals ( capReq . Info . Sim , activeCircuit ) ) ;
2008-01-05 05:16:39 +00:00
2008-10-30 01:50:59 +00:00
info [ "SeedCapability" ] = OSD . FromString ( capsURL ) ;
2007-04-28 20:50:45 +00:00
bytes [ 0 ] = ( byte ) ( simIP % 256 ) ;
bytes [ 1 ] = ( byte ) ( ( simIP > > 8 ) % 256 ) ;
bytes [ 2 ] = ( byte ) ( ( simIP > > 16 ) % 256 ) ;
bytes [ 3 ] = ( byte ) ( ( simIP > > 24 ) % 256 ) ;
2008-10-30 01:50:59 +00:00
info [ "SimIP" ] = OSD . FromBinary ( bytes ) ;
info [ "SimPort" ] = OSD . FromInteger ( simPort ) ;
2007-11-06 09:26:10 +00:00
}
2009-02-08 08:24:35 +00:00
else if ( message = = "EnableSimulator" )
{
OSDMap info = null ;
info = ( OSDMap ) ( ( ( OSDArray ) body [ "SimulatorInfo" ] ) [ 0 ] ) ;
byte [ ] bytes = info [ "IP" ] . AsBinary ( ) ;
uint IP = Utils . BytesToUInt ( bytes ) ;
ushort Port = ( ushort ) info [ "Port" ] . AsInteger ( ) ;
string capsURL = null ;
2020-05-09 12:51:59 -05:00
GenericCheck ( ref IP , ref Port , ref capsURL , Equals ( capReq . Info . Sim , activeCircuit ) ) ;
2009-02-08 08:24:35 +00:00
bytes [ 0 ] = ( byte ) ( IP % 256 ) ;
bytes [ 1 ] = ( byte ) ( ( IP > > 8 ) % 256 ) ;
bytes [ 2 ] = ( byte ) ( ( IP > > 16 ) % 256 ) ;
bytes [ 3 ] = ( byte ) ( ( IP > > 24 ) % 256 ) ;
info [ "IP" ] = OSD . FromBinary ( bytes ) ;
info [ "Port" ] = OSD . FromInteger ( Port ) ;
}
2007-11-06 09:26:10 +00:00
else if ( message = = "EstablishAgentCommunication" )
{
2008-01-05 05:16:39 +00:00
string ipAndPort = body [ "sim-ip-and-port" ] . AsString ( ) ;
2007-11-06 09:26:10 +00:00
string [ ] pieces = ipAndPort . Split ( ':' ) ;
byte [ ] bytes = IPAddress . Parse ( pieces [ 0 ] ) . GetAddressBytes ( ) ;
2008-10-06 22:34:38 +00:00
uint simIP = Utils . BytesToUInt ( bytes ) ;
2007-11-06 09:26:10 +00:00
ushort simPort = ( ushort ) Convert . ToInt32 ( pieces [ 1 ] ) ;
2008-01-05 05:16:39 +00:00
string capsURL = body [ "seed-capability" ] . AsString ( ) ;
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "DEBUG: Got EstablishAgentCommunication for " + ipAndPort + " with seed cap " + capsURL , Helpers . LogLevel . Debug ) ;
2008-01-05 05:16:39 +00:00
2007-11-06 09:26:10 +00:00
GenericCheck ( ref simIP , ref simPort , ref capsURL , false ) ;
2008-10-30 01:50:59 +00:00
body [ "seed-capability" ] = OSD . FromString ( capsURL ) ;
2020-05-09 12:51:59 -05:00
string ipport = $"{new IPAddress(simIP)}:{simPort}" ;
2008-10-30 01:50:59 +00:00
body [ "sim-ip-and-port" ] = OSD . FromString ( ipport ) ;
2008-01-05 05:16:39 +00:00
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "DEBUG: Modified EstablishAgentCommunication to " + body [ "sim-ip-and-port" ] . AsString ( ) + " with seed cap " + capsURL , Helpers . LogLevel . Debug ) ;
2007-11-06 09:26:10 +00:00
}
2007-04-28 20:50:45 +00:00
}
return false ;
}
private void ProxyLogin ( NetworkStream netStream , byte [ ] content )
{
lock ( this )
{
2009-11-03 05:32:49 +00:00
// incase some silly person tries to access with their web browser
if ( content . Length < = 0 )
return ;
2007-04-28 20:50:45 +00:00
// convert the body into an XML-RPC request
XmlRpcRequest request = ( XmlRpcRequest ) ( new XmlRpcRequestDeserializer ( ) ) . Deserialize ( Encoding . UTF8 . GetString ( content ) ) ;
// call the loginRequestDelegate
2009-05-30 00:35:20 +00:00
lock ( loginRequestDelegates )
2009-05-25 19:33:20 +00:00
{
2009-05-30 00:35:20 +00:00
foreach ( XmlRpcRequestDelegate d in loginRequestDelegates )
2007-04-28 20:50:45 +00:00
{
2009-11-12 00:15:59 +00:00
try { d ( this , new XmlRpcRequestEventArgs ( request ) ) ; }
//try { d(request); }
2009-05-30 00:35:20 +00:00
catch ( Exception e ) { OpenMetaverse . Logger . Log ( "Exception in login request delegate" + e , Helpers . LogLevel . Error , e ) ; }
2007-04-28 20:50:45 +00:00
}
2009-05-25 19:33:20 +00:00
}
2007-04-28 20:50:45 +00:00
XmlRpcResponse response ;
try
{
// forward the XML-RPC request to the server
2019-12-04 17:26:35 -06:00
var cts = new CancellationTokenSource ( ) ;
cts . CancelAfter ( TimeSpan . FromSeconds ( 30 ) ) ; // 30 second timeout
response = HttpClient . PostAsXmlRpcAsync ( proxyConfig . remoteLoginUri , request , cts . Token ) . Result ;
2019-12-11 21:00:18 -06:00
cts . Dispose ( ) ;
2007-04-28 20:50:45 +00:00
}
catch ( Exception e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Error during login response" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
return ;
}
2009-05-25 19:33:20 +00:00
System . Collections . Hashtable responseData ;
try
{
responseData = ( System . Collections . Hashtable ) response . Value ;
}
catch ( Exception e )
{
OpenMetaverse . Logger . Log ( e . Message , Helpers . LogLevel . Error ) ;
return ;
}
2007-04-28 20:50:45 +00:00
// 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" ] ;
}
// forward the XML-RPC response to the client
2017-05-16 18:54:22 -05:00
using ( 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" ) ;
using ( XmlTextWriter responseWriter = new XmlTextWriter ( writer ) )
{
XmlRpcResponseSerializer . Singleton . Serialize ( responseWriter , response ) ;
}
}
2009-05-30 00:35:20 +00:00
lock ( loginResponseDelegates )
{
foreach ( XmlRpcResponseDelegate d in loginResponseDelegates )
{
try { d ( response ) ; }
catch ( Exception e ) { OpenMetaverse . Logger . Log ( "Exception in login response delegate" + e , Helpers . LogLevel . Error , e ) ; }
}
}
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
}
}
2008-10-30 01:50:59 +00:00
private void ProxyLoginSD ( NetworkStream netStream , byte [ ] content )
2008-04-11 01:20:50 +00:00
{
2010-08-01 00:32:11 +00:00
lock ( this )
{
2008-04-11 01:20:50 +00:00
AutoResetEvent remoteComplete = new AutoResetEvent ( false ) ;
2009-03-18 17:41:05 +00:00
CapsClient loginRequest = new CapsClient ( proxyConfig . remoteLoginUri ) ;
2008-10-30 01:50:59 +00:00
OSD response = null ;
2008-04-11 01:20:50 +00:00
loginRequest . OnComplete + = new CapsClient . CompleteCallback (
2008-10-30 01:50:59 +00:00
delegate ( CapsClient client , OSD result , Exception error )
2008-04-11 01:20:50 +00:00
{
2010-08-01 00:32:11 +00:00
if ( error = = null )
{
if ( result ! = null & & result . Type = = OSDType . Map )
{
2008-04-11 01:20:50 +00:00
response = result ;
}
}
remoteComplete . Set ( ) ;
}
) ;
2021-09-24 14:05:12 -05:00
loginRequest . PostRequestAsync ( content , "application/llsd+xml" , 1000 * 100 ) ;
2009-05-01 06:04:32 +00:00
remoteComplete . WaitOne ( 1000 * 100 , false ) ;
2008-04-11 01:20:50 +00:00
2010-08-01 00:32:11 +00:00
if ( response = = null )
{
2008-04-11 03:03:23 +00:00
byte [ ] wr = Encoding . ASCII . GetBytes ( "HTTP/1.0 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" ) ;
netStream . Write ( wr , 0 , wr . Length ) ;
2008-04-11 01:20:50 +00:00
return ;
}
2008-10-30 01:50:59 +00:00
OSDMap map = ( OSDMap ) response ;
2008-04-11 01:20:50 +00:00
2008-10-30 01:50:59 +00:00
OSD llsd ;
2008-04-11 03:03:23 +00:00
string sim_port = null , sim_ip = null , seed_capability = null ;
2008-04-11 01:20:50 +00:00
map . TryGetValue ( "sim_port" , out llsd ) ;
2008-04-11 03:03:23 +00:00
if ( llsd ! = null ) sim_port = llsd . AsString ( ) ;
2008-04-11 01:20:50 +00:00
map . TryGetValue ( "sim_ip" , out llsd ) ;
2008-04-11 03:03:23 +00:00
if ( llsd ! = null ) sim_ip = llsd . AsString ( ) ;
2008-04-11 01:20:50 +00:00
map . TryGetValue ( "seed_capability" , out llsd ) ;
2008-04-11 03:03:23 +00:00
if ( llsd ! = null ) seed_capability = llsd . AsString ( ) ;
2008-04-11 01:20:50 +00:00
2010-08-01 00:32:11 +00:00
if ( sim_port = = null | | sim_ip = = null | | seed_capability = = null )
{
2009-07-10 00:24:52 +00:00
if ( map ! = null )
{
2024-06-30 16:35:53 -05:00
OpenMetaverse . Logger . Log ( "Connection to server failed, returned LLSD error follows:\n" + map , Helpers . LogLevel . Error ) ;
2009-07-10 00:24:52 +00:00
}
2008-04-11 03:03:23 +00:00
byte [ ] wr = Encoding . ASCII . GetBytes ( "HTTP/1.0 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" ) ;
netStream . Write ( wr , 0 , wr . Length ) ;
2008-04-11 01:20:50 +00:00
return ;
}
IPEndPoint realSim = new IPEndPoint ( IPAddress . Parse ( sim_ip ) , Convert . ToUInt16 ( sim_port ) ) ;
IPEndPoint fakeSim = ProxySim ( realSim ) ;
2008-10-30 01:50:59 +00:00
map [ "sim_ip" ] = OSD . FromString ( fakeSim . Address . ToString ( ) ) ;
map [ "sim_port" ] = OSD . FromInteger ( fakeSim . Port ) ;
2008-04-11 01:20:50 +00:00
activeCircuit = realSim ;
2008-04-11 03:03:23 +00:00
// start a new proxy session
Reset ( ) ;
2008-04-11 01:20:50 +00:00
CapInfo info = new CapInfo ( seed_capability , activeCircuit , "SeedCapability" ) ;
info . AddDelegate ( new CapsDelegate ( FixupSeedCapsResponse ) ) ;
2008-04-11 03:03:23 +00:00
KnownCaps [ seed_capability ] = info ;
2008-10-30 01:50:59 +00:00
map [ "seed_capability" ] = OSD . FromString ( loginURI + seed_capability ) ;
2008-04-11 01:20:50 +00:00
2008-04-11 03:03:23 +00:00
StreamWriter writer = new StreamWriter ( netStream ) ;
2008-04-11 01:20:50 +00:00
writer . Write ( "HTTP/1.0 200 OK\r\n" ) ;
writer . Write ( "Content-type: application/xml+llsd\r\n" ) ;
writer . Write ( "\r\n" ) ;
2008-10-30 16:54:17 +00:00
writer . Write ( OSDParser . SerializeLLSDXmlString ( response ) ) ;
2008-04-11 01:20:50 +00:00
writer . Close ( ) ;
}
}
2007-04-24 07:08:18 +00:00
/ *
* Sim Proxy
2007-04-28 20:50:45 +00:00
* /
private Socket simFacingSocket ;
2009-02-20 18:05:00 +00:00
public IPEndPoint activeCircuit = null ;
2007-11-06 09:26:10 +00:00
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 > ( ) ;
2009-05-30 00:35:20 +00:00
//private XmlRpcRequestDelegate loginRequestDelegate = null;
//private XmlRpcResponseDelegate loginResponseDelegate = null;
2010-08-01 00:32:11 +00:00
2009-05-30 00:35:20 +00:00
public List < XmlRpcRequestDelegate > loginRequestDelegates = new List < XmlRpcRequestDelegate > ( ) ;
public List < XmlRpcResponseDelegate > loginResponseDelegates = new List < XmlRpcResponseDelegate > ( ) ;
2007-04-28 20:50:45 +00:00
private Dictionary < PacketType , List < PacketDelegate > > incomingDelegates = new Dictionary < PacketType , List < PacketDelegate > > ( ) ;
private Dictionary < PacketType , List < PacketDelegate > > outgoingDelegates = new Dictionary < PacketType , List < PacketDelegate > > ( ) ;
2007-11-06 09:26:10 +00:00
private List < Packet > queuedIncomingInjections = new List < Packet > ( ) ;
private List < Packet > queuedOutgoingInjections = new List < Packet > ( ) ;
2007-04-28 20:50:45 +00:00
// 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 ( ) ;
2009-11-12 00:15:59 +00:00
2009-05-30 00:35:20 +00:00
KnownCaps . Clear ( ) ;
2007-04-28 20:50:45 +00:00
}
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 ( )
{
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 )
{
2010-08-01 00:32:11 +00:00
lock ( this )
2009-05-25 19:00:28 +00:00
try
2007-04-28 20:50:45 +00:00
{
2009-04-23 10:02:39 +00:00
//if (!simFacingSocket.Connected) return;
2007-04-28 20:50:45 +00:00
// pause listening and fetch the packet
bool needsZero = false ;
bool needsCopy = true ;
2024-06-30 18:10:23 -05:00
var length = simFacingSocket . EndReceiveFrom ( ar , ref remoteEndPoint ) ;
2007-04-28 20:50:45 +00:00
2007-11-06 09:26:10 +00:00
if ( proxyHandlers . ContainsKey ( remoteEndPoint ) )
2007-04-28 20:50:45 +00:00
{
// find the proxy responsible for forwarding this packet
SimProxy simProxy = ( SimProxy ) proxyHandlers [ remoteEndPoint ] ;
// interpret the packet according to the SL protocol
int end = length - 1 ;
2024-06-30 18:10:23 -05:00
var packet = Packet . BuildPacket ( receiveBuffer , ref end , zeroBuffer ) ;
2007-04-28 20:50:45 +00:00
// 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
2007-11-06 09:26:10 +00:00
if ( incomingCheckers . ContainsKey ( packet . Type ) )
2007-04-28 20:50:45 +00:00
{
2007-04-24 07:08:18 +00:00
/ * if ( needsZero ) {
length = Helpers . ZeroDecode ( packet . Header . Data , length , zeroBuffer ) ;
packet . Header . Data = zeroBuffer ;
needsZero = false ;
2007-04-28 20:50:45 +00:00
} * /
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 ) )
{
2007-04-24 07:08:18 +00:00
/ * if ( needsZero ) {
length = Helpers . ZeroDecode ( packet . Header . Data , length , zeroBuffer ) ;
packet . Header . Data = zeroBuffer ;
needsCopy = true ;
2007-04-28 20:50:45 +00:00
} * /
2009-05-08 06:53:58 +00:00
if ( packet . Header . AckList ! = null & & needsCopy )
2007-04-28 20:50:45 +00:00
{
2009-05-25 19:00:28 +00:00
uint [ ] newAcks = new uint [ packet . Header . AckList . Length ] ;
Array . Copy ( packet . Header . AckList , 0 , newAcks , 0 , newAcks . Length ) ;
packet . Header . AckList = newAcks ; // FIXME
2007-04-28 20:50:45 +00:00
}
try
{
Packet newPacket = callDelegates ( incomingDelegates , packet , ( IPEndPoint ) remoteEndPoint ) ;
if ( newPacket = = null )
{
2009-05-08 06:38:28 +00:00
if ( packet . Header . Reliable )
2007-04-28 20:50:45 +00:00
simProxy . Inject ( SpoofAck ( oldSequence ) , Direction . Outgoing ) ;
2009-05-08 06:38:28 +00:00
if ( packet . Header . AppendedAcks )
2007-04-28 20:50:45 +00:00
packet = SeparateAck ( packet ) ;
else
packet = null ;
}
else
{
2009-05-08 06:38:28 +00:00
bool oldReliable = packet . Header . Reliable ;
bool newReliable = newPacket . Header . Reliable ;
2007-04-28 20:50:45 +00:00
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 )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Exception in incoming delegate" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
if ( packet ! = null )
simProxy . SendPacket ( packet , false ) ;
}
else
simProxy . SendPacket ( packet , needsZero ) ;
}
else
// ignore packets from unknown peers
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Dropping packet from unknown peer " + remoteEndPoint , Helpers . LogLevel . Warning ) ;
2007-04-28 20:50:45 +00:00
}
catch ( Exception e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Error processing incoming packet from simulator" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
finally
{
// resume listening
2008-07-21 07:20:00 +00:00
try
{
simFacingSocket . BeginReceiveFrom ( receiveBuffer , 0 , receiveBuffer . Length , SocketFlags . None ,
ref remoteEndPoint , new AsyncCallback ( ReceiveFromSim ) , null ) ;
}
catch ( Exception e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Listener Socket Exception" , Helpers . LogLevel . Error , e ) ;
2008-07-21 07:20:00 +00:00
}
2007-04-28 20:50:45 +00:00
}
}
// SendPacket: send a packet to a sim from our fake client endpoint
public void SendPacket ( Packet packet , IPEndPoint endPoint , bool skipZero )
{
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
byte [ ] buffer = packet . ToBytes ( ) ;
2009-05-08 06:38:28 +00:00
if ( skipZero | | ! packet . Header . Zerocoded )
2007-04-28 20:50:45 +00:00
simFacingSocket . SendTo ( buffer , buffer . Length , SocketFlags . None , endPoint ) ;
else
{
int zeroLength = Helpers . ZeroEncode ( buffer , buffer . Length , zeroBuffer ) ;
simFacingSocket . SendTo ( zeroBuffer , zeroLength , SocketFlags . None , endPoint ) ;
2010-08-01 00:32:11 +00:00
}
2007-04-28 20:50:45 +00:00
}
// 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 ;
}
// 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 )
{
2009-04-03 19:58:35 +00:00
seperate . Packets [ i ] = new PacketAckPacket . PacketsBlock ( ) ;
2007-04-28 20:50:45 +00:00
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 ;
2009-05-08 06:38:28 +00:00
int oldAcks = oldPacket . Header . AppendedAcks ? oldPacket . Header . AckList . Length : 0 ;
int newAcks = newPacket . Header . AppendedAcks ? newPacket . Header . AckList . Length : 0 ;
2007-04-28 20:50:45 +00:00
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 specified sim, creating it if it doesn't exist
private IPEndPoint ProxySim ( IPEndPoint simEndPoint )
{
2007-11-06 09:26:10 +00:00
if ( proxyEndPoints . ContainsKey ( simEndPoint ) )
2007-04-28 20:50:45 +00:00
// return the existing proxy
return ( IPEndPoint ) proxyEndPoints [ simEndPoint ] ;
else
{
// return a new proxy
SimProxy simProxy = new SimProxy ( proxyConfig , simEndPoint , this ) ;
IPEndPoint fakeSim = simProxy . LocalEndPoint ( ) ;
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Creating proxy for " + simEndPoint + " at " + fakeSim , Helpers . LogLevel . Info ) ;
2007-04-28 20:50:45 +00:00
simProxy . Run ( ) ;
proxyEndPoints . Add ( simEndPoint , fakeSim ) ;
simProxies . Add ( simEndPoint , simProxy ) ;
return fakeSim ;
}
}
// AddHandler: remember which sim proxy corresponds to a given sim
private void AddHandler ( EndPoint endPoint , SimProxy proxy )
{
proxyHandlers . Add ( endPoint , proxy ) ;
}
// SimProxy: proxy for a single simulator
private class SimProxy
{
2008-10-09 16:48:44 +00:00
//private ProxyConfig proxyConfig;
2007-04-28 20:50:45 +00:00
private IPEndPoint remoteEndPoint ;
private Proxy proxy ;
private Socket socket ;
public uint incomingSequence ;
public uint outgoingSequence ;
2007-11-06 09:26:10 +00:00
private List < uint > incomingInjections ;
private List < uint > outgoingInjections ;
2007-04-28 20:50:45 +00:00
private uint incomingOffset = 0 ;
private uint outgoingOffset = 0 ;
2007-11-06 09:26:10 +00:00
private Dictionary < uint , Packet > incomingAcks ;
private Dictionary < uint , Packet > outgoingAcks ;
private List < uint > incomingSeenAcks ;
private List < uint > outgoingSeenAcks ;
2010-08-01 00:32:11 +00:00
2007-04-28 20:50:45 +00:00
// SimProxy: construct a proxy for a single simulator
public SimProxy ( ProxyConfig proxyConfig , IPEndPoint simEndPoint , Proxy proxy )
{
2008-10-09 16:48:44 +00:00
//this.proxyConfig = proxyConfig;
2007-04-28 20:50:45 +00:00
remoteEndPoint = new IPEndPoint ( simEndPoint . Address , simEndPoint . Port ) ;
this . proxy = proxy ;
socket = new Socket ( AddressFamily . InterNetwork , SocketType . Dgram , ProtocolType . Udp ) ;
socket . Bind ( new IPEndPoint ( proxyConfig . clientFacingAddress , 0 ) ) ;
proxy . AddHandler ( remoteEndPoint , this ) ;
Reset ( ) ;
}
// Reset: start a new session
public void Reset ( )
{
incomingSequence = 0 ;
outgoingSequence = 0 ;
2007-11-06 09:26:10 +00:00
incomingInjections = new List < uint > ( ) ;
outgoingInjections = new List < uint > ( ) ;
incomingAcks = new Dictionary < uint , Packet > ( ) ;
outgoingAcks = new Dictionary < uint , Packet > ( ) ;
incomingSeenAcks = new List < uint > ( ) ;
outgoingSeenAcks = new List < uint > ( ) ;
2007-04-28 20:50:45 +00:00
}
// BackgroundTasks: resend unacknowledged packets and keep data structures clean
private void BackgroundTasks ( )
{
try
{
int tick = 1 ;
int incomingInjectionsPoint = 0 ;
int outgoingInjectionsPoint = 0 ;
int incomingSeenAcksPoint = 0 ;
int outgoingSeenAcksPoint = 0 ;
for ( ; ; Thread . Sleep ( 1000 ) ) lock ( proxy )
2024-06-30 17:38:53 -05:00
{
if ( ( tick = ( tick + 1 ) % 60 ) = = 0 )
2007-04-28 20:50:45 +00:00
{
2024-06-30 17:38:53 -05:00
for ( int i = 0 ; i < incomingInjectionsPoint ; + + i )
2007-04-28 20:50:45 +00:00
{
2024-06-30 17:38:53 -05:00
incomingInjections . RemoveAt ( 0 ) ;
+ + incomingOffset ;
}
incomingInjectionsPoint = incomingInjections . Count ;
2007-04-28 20:50:45 +00:00
2024-06-30 17:38:53 -05:00
for ( int i = 0 ; i < outgoingInjectionsPoint ; + + i )
{
outgoingInjections . RemoveAt ( 0 ) ;
+ + outgoingOffset ;
}
outgoingInjectionsPoint = outgoingInjections . Count ;
2007-04-28 20:50:45 +00:00
2024-06-30 17:38:53 -05:00
for ( int i = 0 ; i < incomingSeenAcksPoint ; + + i )
{
incomingAcks . Remove ( incomingSeenAcks [ 0 ] ) ;
incomingSeenAcks . RemoveAt ( 0 ) ;
}
incomingSeenAcksPoint = incomingSeenAcks . Count ;
2007-04-28 20:50:45 +00:00
2024-06-30 17:38:53 -05:00
for ( int i = 0 ; i < outgoingSeenAcksPoint ; + + i )
{
outgoingAcks . Remove ( outgoingSeenAcks [ 0 ] ) ;
outgoingSeenAcks . RemoveAt ( 0 ) ;
2007-04-28 20:50:45 +00:00
}
2024-06-30 17:38:53 -05:00
outgoingSeenAcksPoint = outgoingSeenAcks . Count ;
}
2007-04-28 20:50:45 +00:00
2024-06-30 17:38:53 -05:00
foreach ( uint id in incomingAcks . Keys )
if ( ! incomingSeenAcks . Contains ( id ) )
{
Packet packet = ( Packet ) incomingAcks [ id ] ;
packet . Header . Resent = true ;
SendPacket ( packet , false ) ;
}
2007-04-28 20:50:45 +00:00
2024-06-30 17:38:53 -05:00
foreach ( uint id in outgoingAcks . Keys )
if ( ! outgoingSeenAcks . Contains ( id ) )
{
Packet packet = ( Packet ) outgoingAcks [ id ] ;
packet . Header . Resent = true ;
proxy . SendPacket ( packet , remoteEndPoint , false ) ;
}
}
2007-04-28 20:50:45 +00:00
}
catch ( Exception e )
{
2009-07-10 00:24:52 +00:00
OpenMetaverse . Logger . Log ( "Exception running BackgroundTasks" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
}
// LocalEndPoint: return the endpoint that the client should communicate with
public IPEndPoint LocalEndPoint ( )
{
return ( IPEndPoint ) socket . LocalEndPoint ;
}
private byte [ ] receiveBuffer = new byte [ 8192 ] ;
private byte [ ] zeroBuffer = new byte [ 8192 ] ;
private EndPoint clientEndPoint = new IPEndPoint ( IPAddress . Any , 0 ) ;
bool firstReceive = true ;
// Run: forward packets from the client to the sim
public void Run ( )
{
Thread backgroundTasks = new Thread ( new ThreadStart ( BackgroundTasks ) ) ;
backgroundTasks . IsBackground = true ;
backgroundTasks . Start ( ) ;
socket . BeginReceiveFrom ( receiveBuffer , 0 , receiveBuffer . Length , SocketFlags . None , ref clientEndPoint , new AsyncCallback ( ReceiveFromClient ) , null ) ;
}
// ReceiveFromClient: packet received from the client
private void ReceiveFromClient ( IAsyncResult ar )
{
2007-11-06 09:26:10 +00:00
lock ( proxy )
{
try
2007-04-28 20:50:45 +00:00
{
// pause listening and fetch the packet
bool needsZero = false ;
bool needsCopy = true ;
2008-08-21 21:51:20 +00:00
int length = 0 ;
2007-04-28 20:50:45 +00:00
2008-08-21 21:51:20 +00:00
try { length = socket . EndReceiveFrom ( ar , ref clientEndPoint ) ; }
catch ( SocketException ) { }
if ( length ! = 0 )
{
// interpret the packet according to the SL protocol
int end = length - 1 ;
Packet packet = OpenMetaverse . Packets . Packet . BuildPacket ( receiveBuffer , ref end , zeroBuffer ) ;
2007-04-28 20:50:45 +00:00
2010-08-01 00:32:11 +00:00
//OpenMetaverse.Logger.Log("-> " + packet.Type + " #" + packet.Header.Sequence, Helpers.LogLevel.Debug);
2007-11-06 09:26:10 +00:00
2008-08-21 21:51:20 +00:00
// check for ACKs we're waiting for
packet = CheckAcks ( packet , Direction . Outgoing , ref length , ref needsCopy ) ;
2007-04-28 20:50:45 +00:00
2008-08-21 21:51:20 +00:00
// modify sequence numbers to account for injections
uint oldSequence = packet . Header . Sequence ;
packet = ModifySequence ( packet , Direction . Outgoing , ref length , ref needsCopy ) ;
2007-04-28 20:50:45 +00:00
2008-08-21 21:51:20 +00:00
// keep track of sequence numbers
if ( packet . Header . Sequence > outgoingSequence )
outgoingSequence = packet . Header . Sequence ;
2007-04-28 20:50:45 +00:00
2008-08-21 21:51:20 +00:00
// check the packet for addresses that need proxying
if ( proxy . outgoingCheckers . ContainsKey ( packet . Type ) )
2007-04-28 20:50:45 +00:00
{
2008-08-21 21:51:20 +00:00
/ * if ( packet . Header . Zerocoded ) {
length = Helpers . ZeroDecode ( packet . Header . Data , length , zeroBuffer ) ;
packet . Header . Data = zeroBuffer ;
needsZero = false ;
} * /
Packet newPacket = ( ( AddressChecker ) proxy . outgoingCheckers [ packet . Type ] ) ( packet ) ;
SwapPacket ( packet , newPacket ) ;
packet = newPacket ;
needsCopy = false ;
2007-04-28 20:50:45 +00:00
}
2008-08-21 21:51:20 +00:00
// pass the packet to any callback delegates
if ( proxy . outgoingDelegates . ContainsKey ( packet . Type ) )
2007-04-28 20:50:45 +00:00
{
2009-05-08 06:53:58 +00:00
if ( packet . Header . AckList ! = null & & needsCopy )
2007-04-28 20:50:45 +00:00
{
2010-08-01 00:32:11 +00:00
uint [ ] newAcks = new uint [ packet . Header . AckList . Length ] ;
Array . Copy ( packet . Header . AckList , 0 , newAcks , 0 , newAcks . Length ) ;
packet . Header . AckList = newAcks ; // FIXME
2008-08-21 21:51:20 +00:00
}
2007-04-28 20:50:45 +00:00
2008-08-21 21:51:20 +00:00
try
{
Packet newPacket = proxy . callDelegates ( proxy . outgoingDelegates , packet , remoteEndPoint ) ;
if ( newPacket = = null )
{
2009-05-08 06:38:28 +00:00
if ( packet . Header . Reliable )
2008-08-21 21:51:20 +00:00
Inject ( proxy . SpoofAck ( oldSequence ) , Direction . Incoming ) ;
2009-05-08 06:38:28 +00:00
if ( packet . Header . AppendedAcks )
2008-08-21 21:51:20 +00:00
packet = proxy . SeparateAck ( packet ) ;
else
packet = null ;
}
2007-04-28 20:50:45 +00:00
else
2008-08-21 21:51:20 +00:00
{
2009-05-08 06:38:28 +00:00
bool oldReliable = packet . Header . Reliable ;
bool newReliable = newPacket . Header . Reliable ;
2008-08-21 21:51:20 +00:00
if ( oldReliable & & ! newReliable )
Inject ( proxy . SpoofAck ( oldSequence ) , Direction . Incoming ) ;
else if ( ! oldReliable & & newReliable )
WaitForAck ( packet , Direction . Outgoing ) ;
SwapPacket ( packet , newPacket ) ;
packet = newPacket ;
}
2007-04-28 20:50:45 +00:00
}
2008-08-21 21:51:20 +00:00
catch ( Exception e )
2007-04-28 20:50:45 +00:00
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "exception in outgoing delegate" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
2008-08-21 21:51:20 +00:00
if ( packet ! = null )
proxy . SendPacket ( packet , remoteEndPoint , false ) ;
2007-04-28 20:50:45 +00:00
}
2008-08-21 21:51:20 +00:00
else
proxy . SendPacket ( packet , remoteEndPoint , needsZero ) ;
// send any packets queued for injection
if ( firstReceive )
2007-04-28 20:50:45 +00:00
{
2008-08-21 21:51:20 +00:00
firstReceive = false ;
foreach ( Packet queuedPacket in proxy . queuedIncomingInjections )
Inject ( queuedPacket , Direction . Incoming ) ;
proxy . queuedIncomingInjections = new List < Packet > ( ) ;
2007-04-28 20:50:45 +00:00
}
}
}
catch ( Exception e )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Proxy error sending packet" , Helpers . LogLevel . Error , e ) ;
2007-04-28 20:50:45 +00:00
}
finally
{
// resume listening
2008-07-21 07:20:00 +00:00
try
{
socket . BeginReceiveFrom ( receiveBuffer , 0 , receiveBuffer . Length , SocketFlags . None ,
ref clientEndPoint , new AsyncCallback ( ReceiveFromClient ) , null ) ;
}
2009-05-25 19:00:28 +00:00
catch ( SocketException e )
2008-07-21 07:20:00 +00:00
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( "Socket Shutdown: " + e . SocketErrorCode , Helpers . LogLevel . Warning ) ;
2008-07-21 07:20:00 +00:00
}
2007-04-28 20:50:45 +00:00
}
2007-11-06 09:26:10 +00:00
}
2007-04-28 20:50:45 +00:00
}
// SendPacket: send a packet from the sim to the client via our fake sim endpoint
public void SendPacket ( Packet packet , bool skipZero )
{
byte [ ] buffer = packet . ToBytes ( ) ;
2009-05-08 06:38:28 +00:00
if ( skipZero | | ! packet . Header . Zerocoded )
2007-04-28 20:50:45 +00:00
socket . SendTo ( buffer , buffer . Length , SocketFlags . None , clientEndPoint ) ;
else
{
int zeroLength = Helpers . ZeroEncode ( buffer , buffer . Length , zeroBuffer ) ;
socket . SendTo ( zeroBuffer , zeroLength , SocketFlags . None , clientEndPoint ) ;
}
}
// Inject: inject a packet
public void Inject ( Packet packet , Direction direction )
{
if ( direction = = Direction . Incoming )
{
if ( firstReceive )
{
proxy . queuedIncomingInjections . Add ( packet ) ;
return ;
}
incomingInjections . Add ( + + incomingSequence ) ;
packet . Header . Sequence = incomingSequence ;
}
else
{
outgoingInjections . Add ( + + outgoingSequence ) ;
packet . Header . Sequence = outgoingSequence ;
}
2009-05-08 06:38:28 +00:00
if ( packet . Header . Reliable )
2007-04-28 20:50:45 +00:00
WaitForAck ( packet , direction ) ;
if ( direction = = Direction . Incoming )
{
byte [ ] buffer = packet . ToBytes ( ) ;
2009-05-08 06:38:28 +00:00
if ( ! packet . Header . Zerocoded )
2007-04-28 20:50:45 +00:00
socket . SendTo ( buffer , buffer . Length , SocketFlags . None , clientEndPoint ) ;
else
{
int zeroLength = Helpers . ZeroEncode ( buffer , buffer . Length , zeroBuffer ) ;
socket . SendTo ( zeroBuffer , zeroLength , SocketFlags . None , clientEndPoint ) ;
}
}
else
proxy . SendPacket ( packet , remoteEndPoint , false ) ;
}
// WaitForAck: take care of resending a packet until it's ACKed
public void WaitForAck ( Packet packet , Direction direction )
{
2007-11-06 09:26:10 +00:00
Dictionary < uint , Packet > table = direction = = Direction . Incoming ? incomingAcks : outgoingAcks ;
2007-04-28 20:50:45 +00:00
table . Add ( packet . Header . Sequence , packet ) ;
}
// CheckAcks: check for and remove ACKs of packets we've injected
public Packet CheckAcks ( Packet packet , Direction direction , ref int length , ref bool needsCopy )
{
2007-11-06 09:26:10 +00:00
Dictionary < uint , Packet > acks = direction = = Direction . Incoming ? outgoingAcks : incomingAcks ;
List < uint > seenAcks = direction = = Direction . Incoming ? outgoingSeenAcks : incomingSeenAcks ;
2007-04-28 20:50:45 +00:00
if ( acks . Count = = 0 )
return packet ;
// check for embedded ACKs
if ( packet . Type = = PacketType . PacketAck )
{
bool changed = false ;
List < PacketAckPacket . PacketsBlock > newPacketBlocks = new List < PacketAckPacket . PacketsBlock > ( ) ;
foreach ( PacketAckPacket . PacketsBlock pb in ( ( PacketAckPacket ) packet ) . Packets )
{
uint id = pb . ID ;
2007-11-06 09:26:10 +00:00
if ( acks . ContainsKey ( id ) )
2007-04-28 20:50:45 +00:00
{
2008-03-13 00:18:05 +00:00
acks . Remove ( id ) ;
2007-04-28 20:50:45 +00:00
seenAcks . Add ( id ) ;
changed = true ;
}
else
2009-07-10 00:24:52 +00:00
{
2007-04-28 20:50:45 +00:00
newPacketBlocks . Add ( pb ) ;
2009-07-10 00:24:52 +00:00
}
2007-04-28 20:50:45 +00:00
}
if ( changed )
{
PacketAckPacket newPacket = new PacketAckPacket ( ) ;
newPacket . Packets = new PacketAckPacket . PacketsBlock [ newPacketBlocks . Count ] ;
int a = 0 ;
foreach ( PacketAckPacket . PacketsBlock pb in newPacketBlocks )
{
newPacket . Packets [ a + + ] = pb ;
}
SwapPacket ( packet , ( Packet ) newPacket ) ;
packet = newPacket ;
needsCopy = false ;
}
}
// check for appended ACKs
2009-05-08 06:38:28 +00:00
if ( packet . Header . AppendedAcks )
2007-04-28 20:50:45 +00:00
{
int ackCount = packet . Header . AckList . Length ;
for ( int i = 0 ; i < ackCount ; )
{
uint ackID = packet . Header . AckList [ i ] ; // FIXME FIXME FIXME
2009-07-10 00:24:52 +00:00
2007-11-06 09:26:10 +00:00
if ( acks . ContainsKey ( ackID ) )
2007-04-28 20:50:45 +00:00
{
uint [ ] newAcks = new uint [ ackCount - 1 ] ;
Array . Copy ( packet . Header . AckList , 0 , newAcks , 0 , i ) ;
Array . Copy ( packet . Header . AckList , i + 1 , newAcks , i , ackCount - i - 1 ) ;
packet . Header . AckList = newAcks ;
- - ackCount ;
2008-03-13 00:18:05 +00:00
acks . Remove ( ackID ) ;
2007-04-28 20:50:45 +00:00
seenAcks . Add ( ackID ) ;
needsCopy = false ;
}
else
+ + i ;
}
if ( ackCount = = 0 )
{
2009-05-08 06:38:28 +00:00
packet . Header . AppendedAcks = false ;
2022-04-19 18:22:05 -05:00
packet . Header . AckList = Array . Empty < uint > ( ) ;
2007-04-28 20:50:45 +00:00
}
}
return packet ;
}
// ModifySequence: modify a packet's sequence number and ACK IDs to account for injections
public Packet ModifySequence ( Packet packet , Direction direction , ref int length , ref bool needsCopy )
{
2007-11-06 09:26:10 +00:00
List < uint > ourInjections = direction = = Direction . Outgoing ? outgoingInjections : incomingInjections ;
List < uint > theirInjections = direction = = Direction . Incoming ? outgoingInjections : incomingInjections ;
2007-04-28 20:50:45 +00:00
uint ourOffset = direction = = Direction . Outgoing ? outgoingOffset : incomingOffset ;
uint theirOffset = direction = = Direction . Incoming ? outgoingOffset : incomingOffset ;
uint newSequence = ( uint ) ( packet . Header . Sequence + ourOffset ) ;
foreach ( uint injection in ourInjections )
if ( newSequence > = injection )
+ + newSequence ;
2009-07-10 00:24:52 +00:00
2007-04-28 20:50:45 +00:00
packet . Header . Sequence = newSequence ;
2009-05-08 06:38:28 +00:00
if ( packet . Header . AppendedAcks )
2007-04-28 20:50:45 +00:00
{
int ackCount = packet . Header . AckList . Length ;
for ( int i = 0 ; i < ackCount ; + + i )
{
2008-10-09 16:48:44 +00:00
//int offset = length - (ackCount - i) * 4 - 1;
2007-04-28 20:50:45 +00:00
uint ackID = packet . Header . AckList [ i ] - theirOffset ;
2009-07-10 00:24:52 +00:00
2007-04-28 20:50:45 +00:00
for ( int j = theirInjections . Count - 1 ; j > = 0 ; - - j )
if ( ackID > = ( uint ) theirInjections [ j ] )
- - ackID ;
2009-07-10 00:24:52 +00:00
2007-04-28 20:50:45 +00:00
packet . Header . AckList [ i ] = ackID ;
}
}
if ( packet . Type = = PacketType . PacketAck )
{
PacketAckPacket pap = ( PacketAckPacket ) packet ;
foreach ( PacketAckPacket . PacketsBlock pb in pap . Packets )
{
uint ackID = ( uint ) pb . ID - theirOffset ;
2009-07-10 00:24:52 +00:00
2007-04-28 20:50:45 +00:00
for ( int i = theirInjections . Count - 1 ; i > = 0 ; - - i )
if ( ackID > = ( uint ) theirInjections [ i ] )
- - ackID ;
2009-07-10 00:24:52 +00:00
2007-04-28 20:50:45 +00:00
pb . ID = ackID ;
}
2010-08-01 00:32:11 +00:00
2009-05-08 06:38:28 +00:00
switch ( packet . Header . Frequency )
{
case PacketFrequency . High : length = 7 ; break ;
case PacketFrequency . Medium : length = 8 ; break ;
case PacketFrequency . Low : length = 10 ; break ;
}
2007-04-28 20:50:45 +00:00
needsCopy = false ;
}
return packet ;
}
}
// Checkers swap proxy addresses in for real addresses. A few constraints:
// - Checkers must not alter the incoming packet.
// - Checkers must return a freshly built packet, even if nothing's changed.
// - The incoming packet's buffer may be longer than the length of the data it contains.
// - The incoming packet's buffer must not be used after the checker returns.
// This is all because checkers may be operating on data that's still in a scratch buffer.
delegate Packet AddressChecker ( Packet packet ) ;
2007-11-06 09:26:10 +00:00
Dictionary < PacketType , AddressChecker > incomingCheckers = new Dictionary < PacketType , AddressChecker > ( ) ;
Dictionary < PacketType , AddressChecker > outgoingCheckers = new Dictionary < PacketType , AddressChecker > ( ) ;
2007-04-28 20:50:45 +00:00
// InitializeAddressCheckers: initialize delegates that check packets for addresses that need proxying
private void InitializeAddressCheckers ( )
{
// TODO: what do we do with mysteries and empty IPs?
AddMystery ( PacketType . OpenCircuit ) ;
//AddMystery(PacketType.AgentPresenceResponse);
incomingCheckers . Add ( PacketType . TeleportFinish , new AddressChecker ( CheckTeleportFinish ) ) ;
incomingCheckers . Add ( PacketType . CrossedRegion , new AddressChecker ( CheckCrossedRegion ) ) ;
incomingCheckers . Add ( PacketType . EnableSimulator , new AddressChecker ( CheckEnableSimulator ) ) ;
//incomingCheckers.Add("UserLoginLocationReply", new AddressChecker(CheckUserLoginLocationReply));
}
// AddMystery: add a checker delegate that logs packets we're watching for development purposes
private void AddMystery ( PacketType type )
{
incomingCheckers . Add ( type , new AddressChecker ( LogIncomingMysteryPacket ) ) ;
outgoingCheckers . Add ( type , new AddressChecker ( LogOutgoingMysteryPacket ) ) ;
}
// GenericCheck: replace the sim address in a packet with our proxy address
private void GenericCheck ( ref uint simIP , ref ushort simPort , ref string simCaps , bool active )
{
IPAddress sim_ip = new IPAddress ( ( long ) simIP ) ;
IPEndPoint realSim = new IPEndPoint ( sim_ip , Convert . ToInt32 ( simPort ) ) ;
IPEndPoint fakeSim = ProxySim ( realSim ) ;
simPort = ( ushort ) fakeSim . Port ;
byte [ ] bytes = fakeSim . Address . GetAddressBytes ( ) ;
2008-10-06 22:34:38 +00:00
simIP = Utils . BytesToUInt ( bytes ) ;
2019-06-08 17:58:54 -05:00
if ( ! string . IsNullOrEmpty ( simCaps ) )
2007-04-28 20:50:45 +00:00
{
CapInfo info = new CapInfo ( simCaps , realSim , "SeedCapability" ) ;
info . AddDelegate ( new CapsDelegate ( FixupSeedCapsResponse ) ) ;
lock ( this )
{
KnownCaps [ simCaps ] = info ;
}
simCaps = loginURI + simCaps ;
}
if ( active )
activeCircuit = realSim ;
}
// CheckTeleportFinish: check TeleportFinish packets
private Packet CheckTeleportFinish ( Packet packet )
{
TeleportFinishPacket tfp = ( TeleportFinishPacket ) packet ;
string simCaps = Encoding . UTF8 . GetString ( tfp . Info . SeedCapability ) . Replace ( "\0" , "" ) ;
GenericCheck ( ref tfp . Info . SimIP , ref tfp . Info . SimPort , ref simCaps , true ) ;
2008-08-12 22:38:02 +00:00
tfp . Info . SeedCapability = Utils . StringToBytes ( simCaps ) ;
2007-04-28 20:50:45 +00:00
return ( Packet ) tfp ;
}
// CheckEnableSimulator: check EnableSimulator packets
private Packet CheckEnableSimulator ( Packet packet )
{
EnableSimulatorPacket esp = ( EnableSimulatorPacket ) packet ;
string simCaps = null ;
GenericCheck ( ref esp . SimulatorInfo . IP , ref esp . SimulatorInfo . Port , ref simCaps , false ) ;
return ( Packet ) esp ;
}
// CheckCrossedRegion: check CrossedRegion packets
private Packet CheckCrossedRegion ( Packet packet )
{
CrossedRegionPacket crp = ( CrossedRegionPacket ) packet ;
string simCaps = Encoding . UTF8 . GetString ( crp . RegionData . SeedCapability ) . Replace ( "\0" , "" ) ;
GenericCheck ( ref crp . RegionData . SimIP , ref crp . RegionData . SimPort , ref simCaps , true ) ;
2008-08-12 22:38:02 +00:00
crp . RegionData . SeedCapability = Utils . StringToBytes ( simCaps ) ;
2007-04-28 20:50:45 +00:00
return ( Packet ) crp ;
}
// LogPacket: log a packet dump
private Packet LogPacket ( Packet packet , string type )
{
2009-05-25 19:00:28 +00:00
OpenMetaverse . Logger . Log ( type + " packet:\n" + packet , Helpers . LogLevel . Info ) ;
2007-04-28 20:50:45 +00:00
return packet ;
}
// LogIncomingMysteryPacket: log an incoming packet we're watching for development purposes
private Packet LogIncomingMysteryPacket ( Packet packet )
{
return LogPacket ( packet , "incoming mystery" ) ;
}
// LogOutgoingMysteryPacket: log an outgoing packet we're watching for development purposes
private Packet LogOutgoingMysteryPacket ( Packet packet )
{
2010-08-01 00:32:11 +00:00
return LogPacket ( packet , "outgoing mystery" ) ;
2007-04-28 20:50:45 +00:00
}
2009-05-30 00:35:20 +00:00
public void AddLoginRequestDelegate ( XmlRpcRequestDelegate xmlRpcRequestDelegate )
{
2010-08-01 00:32:11 +00:00
lock ( loginRequestDelegates )
if ( ! loginRequestDelegates . Contains ( xmlRpcRequestDelegate ) )
2009-05-30 00:35:20 +00:00
loginRequestDelegates . Add ( xmlRpcRequestDelegate ) ;
2010-08-01 00:32:11 +00:00
2009-05-30 00:35:20 +00:00
}
public void AddLoginResponseDelegate ( XmlRpcResponseDelegate xmlRpcResponseDelegate )
{
lock ( loginResponseDelegates )
if ( ! loginResponseDelegates . Contains ( xmlRpcResponseDelegate ) )
loginResponseDelegates . Add ( xmlRpcResponseDelegate ) ;
}
2007-04-28 20:50:45 +00:00
}
// Describes the data format of a capability
public enum CapsDataFormat
{
Binary = 0 ,
2009-07-10 00:24:52 +00:00
OSD = 1
2007-04-28 20:50:45 +00:00
}
// Describes a caps URI
public class CapInfo
{
private List < CapsDelegate > Delegates = new List < CapsDelegate > ( ) ;
public CapInfo ( string URI , IPEndPoint Sim , string CapType )
:
2009-07-10 00:24:52 +00:00
this ( URI , Sim , CapType , CapsDataFormat . OSD , CapsDataFormat . OSD ) { }
2007-04-28 20:50:45 +00:00
public CapInfo ( string URI , IPEndPoint Sim , string CapType , CapsDataFormat ReqFmt , CapsDataFormat RespFmt )
{
2021-12-09 14:21:48 -06:00
this . URI = URI ; this . Sim = Sim ; this . CapType = CapType ; this . ReqFmt = ReqFmt ; this . RespFmt = RespFmt ;
2007-04-28 20:50:45 +00:00
}
2021-12-09 14:21:48 -06:00
public string URI { get ; }
public string CapType { get ; /* EventQueueGet, etc */ }
public IPEndPoint Sim { get ; }
public CapsDataFormat ReqFmt { get ; /* expected request format */ }
public CapsDataFormat RespFmt { get ; /* expected response format */ }
2007-04-28 20:50:45 +00:00
public void AddDelegate ( CapsDelegate deleg )
{
lock ( this )
{
if ( ! Delegates . Contains ( deleg ) )
{
Delegates . Add ( deleg ) ;
}
}
}
public void RemoveDelegate ( CapsDelegate deleg )
{
lock ( this )
{
2024-06-30 17:23:29 -05:00
Delegates . Remove ( deleg ) ;
2007-04-28 20:50:45 +00:00
}
}
// inefficient, but avoids potential deadlocks.
public List < CapsDelegate > GetDelegates ( )
{
lock ( this )
{
return new List < CapsDelegate > ( Delegates ) ;
}
}
}
// Information associated with a caps request/response
public class CapsRequest
{
public CapsRequest ( CapInfo info )
{
Info = info ;
}
public readonly CapInfo Info ;
// The request
2008-10-30 01:50:59 +00:00
public OSD Request = null ;
2007-04-28 20:50:45 +00:00
// The corresponding response
2008-10-30 01:50:59 +00:00
public OSD Response = null ;
2009-04-22 00:01:30 +00:00
public byte [ ] RawRequest = null ;
public byte [ ] RawResponse = null ;
2009-04-23 10:02:39 +00:00
public WebHeaderCollection RequestHeaders = new WebHeaderCollection ( ) ;
public WebHeaderCollection ResponseHeaders = new WebHeaderCollection ( ) ;
2010-08-01 00:32:11 +00:00
2010-09-03 16:13:35 +00:00
public string FullUri = string . Empty ;
2007-04-28 20:50:45 +00:00
}
// XmlRpcRequestDelegate: specifies a delegate to be called for XML-RPC requests
2009-11-12 00:15:59 +00:00
public delegate void XmlRpcRequestDelegate ( object sender , XmlRpcRequestEventArgs e ) ;
2007-04-28 20:50:45 +00:00
// XmlRpcResponseDelegate: specifies a delegate to be called for XML-RPC responses
public delegate void XmlRpcResponseDelegate ( XmlRpcResponse response ) ;
// PacketDelegate: specifies a delegate to be called when a packet passes through the proxy
public delegate Packet PacketDelegate ( Packet packet , IPEndPoint endPoint ) ;
// Delegate for a caps request. Generally called twice - first with stage = CapsStage.Request
// before the request is sent, then with stage = CapsStage.Response when the response is
// received. Returning true causes all the subsequent delegates in that stage to be skipped,
// and in the case of CapsStage.Request also prevents the request being forwarded. In this
// case, you should set req.Response to the response you want to return.
// Can modify req.Request and req.Response, with the expected effects.
public delegate bool CapsDelegate ( CapsRequest req , CapsStage stage ) ;
// Direction: specifies whether a packet is going to the client (Incoming) or to a sim (Outgoing)
public enum Direction
{
Incoming ,
Outgoing
}
public enum CapsStage
{
Request ,
Response
}
2009-11-12 00:15:59 +00:00
public class XmlRpcRequestEventArgs : EventArgs
{
public XmlRpcRequest m_Request ;
public XmlRpcRequestEventArgs ( XmlRpcRequest request )
{
this . m_Request = request ;
}
}
2007-04-28 20:50:45 +00:00
}
2007-06-03 18:36:07 +00:00