438 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
| /*
 | |
| Copyright (c) OpenSim project, http://osgrid.org/
 | |
| *
 | |
| * 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.
 | |
| *     * Redistributions in binary form must reproduce the above copyright
 | |
| *       notice, this list of conditions and the following disclaimer in the
 | |
| *       documentation and/or other materials provided with the distribution.
 | |
| *     * Neither the name of the <organization> 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 <copyright holder> ``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 <copyright holder> 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.
 | |
| */
 | |
| 
 | |
| using System;
 | |
| using System.Collections;
 | |
| using System.Collections.Generic;
 | |
| using libsecondlife;
 | |
| using libsecondlife.Packets;
 | |
| using Nwc.XmlRpc;
 | |
| using System.Net;
 | |
| using System.Net.Sockets;
 | |
| using System.IO;
 | |
| using System.Threading;
 | |
| using System.Timers;
 | |
| using OpenSim.Framework.Interfaces;
 | |
| using OpenSim.Framework.Assets;
 | |
| using OpenSim.Framework.Inventory;
 | |
| using OpenSim.Framework.Utilities;
 | |
| using OpenSim.world;
 | |
| using OpenSim.Assets;
 | |
| 
 | |
| namespace OpenSim
 | |
| {
 | |
|     public delegate bool PacketMethod(SimComms remoteSim, Packet packet);
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Handles sim<->sim communications
 | |
|     /// Constructor takes a single Packet and authenticates everything
 | |
|     /// </summary>
 | |
|     public class SimComms
 | |
|     {
 | |
|         public LLUUID SimUUID;
 | |
| 	public bool m_child;
 | |
|         public uint CircuitCode;
 | |
|         private UseCircuitCodePacket cirpack;
 | |
|         public Thread ClientThread;
 | |
|         public EndPoint simEP;
 | |
|         private BlockingQueue<QueItem> PacketQueue;
 | |
|         private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
 | |
|         private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
 | |
|         private System.Timers.Timer AckTimer;
 | |
|         private uint Sequence = 0;
 | |
|         private object SequenceLock = new object();
 | |
|         private const int MAX_APPENDED_ACKS = 10;
 | |
|         private const int RESEND_TIMEOUT = 4000;
 | |
|         private const int MAX_SEQUENCE = 0xFFFFFF;
 | |
|         private bool debug = false;
 | |
|         private World m_world;
 | |
|         private Dictionary<uint, SimComms> m_simThreads;
 | |
|         private IGridServer m_gridServer;
 | |
|         private IUserServer m_userServer = null;
 | |
|         private OpenSimNetworkHandler m_application;
 | |
|         //private bool m_sandboxMode;	// I can think of no reason why we'd ever have this set in sandbox mode
 | |
| 
 | |
|         protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all sims
 | |
| 
 | |
|         protected Dictionary<PacketType, PacketMethod> m_packetHandlers = new Dictionary<PacketType, PacketMethod>(); //local handlers for this instance 
 | |
| 
 | |
|         public IUserServer UserServer
 | |
|         {
 | |
|             set
 | |
|             {
 | |
|                 this.m_userServer = value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public SimComms(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, World world, Dictionary<uint, SimComms> simThreads, IGridServer gridServer, OpenSimNetworkHandler application)
 | |
|         {
 | |
|             m_world = world;
 | |
|             m_clientThreads = clientThreads;
 | |
|             m_gridServer = gridServer;
 | |
|             m_application = application;
 | |
| 
 | |
|             OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs - Started up new thread to handle incoming request");
 | |
|             cirpack = initialcirpack;
 | |
|             simEP = remoteEP;
 | |
| 
 | |
|             PacketQueue = new BlockingQueue<QueItem>();
 | |
| 
 | |
|             AckTimer = new System.Timers.Timer(500);
 | |
|             AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed);
 | |
|             AckTimer.Start();
 | |
| 
 | |
|             this.RegisterLocalPacketHandlers();
 | |
| 
 | |
|             ClientThread = new Thread(new ThreadStart(AuthUser));
 | |
|             ClientThread.IsBackground = true;
 | |
|             ClientThread.Start();
 | |
|         }
 | |
| 
 | |
|         protected virtual void RegisterLocalPacketHandlers()
 | |
|         {
 | |
| /*            this.AddLocalPacketHandler(PacketType.LogoutRequest, this.Logout);
 | |
|             this.AddLocalPacketHandler(PacketType.AgentCachedTexture, this.AgentTextureCached);
 | |
|             this.AddLocalPacketHandler(PacketType.MultipleObjectUpdate, this.MultipleObjUpdate);*/
 | |
|         }
 | |
| 
 | |
|         public static bool AddPacketHandler(PacketType packetType, PacketMethod handler)
 | |
|         {
 | |
|             bool result = false;
 | |
|             lock (PacketHandlers)
 | |
|             {
 | |
|                 if (!PacketHandlers.ContainsKey(packetType))
 | |
|                 {
 | |
|                     PacketHandlers.Add(packetType, handler);
 | |
|                     result = true;
 | |
|                 }
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler)
 | |
|         {
 | |
|             bool result = false;
 | |
|             lock (m_packetHandlers)
 | |
|             {
 | |
|                 if (!m_packetHandlers.ContainsKey(packetType))
 | |
|                 {
 | |
|                     m_packetHandlers.Add(packetType, handler);
 | |
|                     result = true;
 | |
|                 }
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         protected virtual bool ProcessPacketMethod(Packet packet)
 | |
|         {
 | |
|             bool result = false;
 | |
|             bool found = false;
 | |
|             PacketMethod method;
 | |
|             if (m_packetHandlers.TryGetValue(packet.Type, out method))
 | |
|             {
 | |
|                 //there is a local handler for this packet type
 | |
|                 result = method(this, packet);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 //there is not a local handler so see if there is a Global handler
 | |
|                 lock (PacketHandlers)
 | |
|                 {
 | |
|                     found = PacketHandlers.TryGetValue(packet.Type, out method);
 | |
|                 }
 | |
|                 if (found)
 | |
|                 {
 | |
|                     result = method(this, packet);
 | |
|                 }
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         private void ack_pack(Packet Pack)
 | |
|         {
 | |
|             if (Pack.Header.Reliable)
 | |
|             {
 | |
|                 libsecondlife.Packets.PacketAckPacket ack_it = new PacketAckPacket();
 | |
|                 ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
 | |
|                 ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
 | |
|                 ack_it.Packets[0].ID = Pack.Header.Sequence;
 | |
|                 ack_it.Header.Reliable = false;
 | |
| 
 | |
|                 OutPacket(ack_it);
 | |
| 
 | |
|             }
 | |
|             /*
 | |
|             if (Pack.Header.Reliable)
 | |
|             {
 | |
|                 lock (PendingAcks)
 | |
|                 {
 | |
|                     uint sequence = (uint)Pack.Header.Sequence;
 | |
|                     if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; }
 | |
|                 }
 | |
|             }*/
 | |
|         }
 | |
| 
 | |
|         protected virtual void ProcessInPacket(Packet Pack)
 | |
|         {
 | |
|             ack_pack(Pack);
 | |
| 
 | |
|             if (this.ProcessPacketMethod(Pack))
 | |
|             {
 | |
|                 //there is a handler registered that handled this packet type 
 | |
|                 return;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 System.Text.Encoding _enc = System.Text.Encoding.ASCII;
 | |
| 
 | |
|                 switch (Pack.Type)
 | |
|                 {
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void ResendUnacked()
 | |
|         {
 | |
|             int now = Environment.TickCount;
 | |
| 
 | |
|             lock (NeedAck)
 | |
|             {
 | |
|                 foreach (Packet packet in NeedAck.Values)
 | |
|                 {
 | |
|                     if ((now - packet.TickCount > RESEND_TIMEOUT) && (!packet.Header.Resent))
 | |
|                     {
 | |
|                         OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Resending " + packet.Type.ToString() + " packet, " +
 | |
|                          (now - packet.TickCount) + "ms have passed");
 | |
| 
 | |
|                         packet.Header.Resent = true;
 | |
|                         OutPacket(packet);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void SendAcks()
 | |
|         {
 | |
|             lock (PendingAcks)
 | |
|             {
 | |
|                 if (PendingAcks.Count > 0)
 | |
|                 {
 | |
|                     if (PendingAcks.Count > 250)
 | |
|                     {
 | |
|                         // FIXME: Handle the odd case where we have too many pending ACKs queued up
 | |
|                         OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Too many ACKs queued up!");
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     //OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Sending PacketAck");
 | |
| 
 | |
| 
 | |
|                     int i = 0;
 | |
|                     PacketAckPacket acks = new PacketAckPacket();
 | |
|                     acks.Packets = new PacketAckPacket.PacketsBlock[PendingAcks.Count];
 | |
| 
 | |
|                     foreach (uint ack in PendingAcks.Values)
 | |
|                     {
 | |
|                         acks.Packets[i] = new PacketAckPacket.PacketsBlock();
 | |
|                         acks.Packets[i].ID = ack;
 | |
|                         i++;
 | |
|                     }
 | |
| 
 | |
|                     acks.Header.Reliable = false;
 | |
|                     OutPacket(acks);
 | |
| 
 | |
|                     PendingAcks.Clear();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea)
 | |
|         {
 | |
|             SendAcks();
 | |
|             ResendUnacked();
 | |
|         }
 | |
| 
 | |
|         protected virtual void ProcessOutPacket(Packet Pack)
 | |
|         {
 | |
| 
 | |
|             // Keep track of when this packet was sent out
 | |
|             Pack.TickCount = Environment.TickCount;
 | |
| 
 | |
|             if (!Pack.Header.Resent)
 | |
|             {
 | |
|                 // Set the sequence number
 | |
|                 lock (SequenceLock)
 | |
|                 {
 | |
|                     if (Sequence >= MAX_SEQUENCE)
 | |
|                         Sequence = 1;
 | |
|                     else
 | |
|                         Sequence++;
 | |
|                     Pack.Header.Sequence = Sequence;
 | |
|                 }
 | |
| 
 | |
|                 if (Pack.Header.Reliable)  //DIRTY HACK
 | |
|                 {
 | |
|                     lock (NeedAck)
 | |
|                     {
 | |
|                         if (!NeedAck.ContainsKey(Pack.Header.Sequence))
 | |
|                         {
 | |
|                             NeedAck.Add(Pack.Header.Sequence, Pack);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             //  Client.Log("Attempted to add a duplicate sequence number (" +
 | |
|                             //     packet.Header.Sequence + ") to the NeedAck dictionary for packet type " +
 | |
|                             //      packet.Type.ToString(), Helpers.LogLevel.Warning);
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     // Don't append ACKs to resent packets, in case that's what was causing the
 | |
|                     // delivery to fail
 | |
|                     if (!Pack.Header.Resent)
 | |
|                     {
 | |
|                         // Append any ACKs that need to be sent out to this packet
 | |
|                         lock (PendingAcks)
 | |
|                         {
 | |
|                             if (PendingAcks.Count > 0 && PendingAcks.Count < MAX_APPENDED_ACKS &&
 | |
|                                 Pack.Type != PacketType.PacketAck &&
 | |
|                                 Pack.Type != PacketType.LogoutRequest)
 | |
|                             {
 | |
|                                 Pack.Header.AckList = new uint[PendingAcks.Count];
 | |
|                                 int i = 0;
 | |
| 
 | |
|                                 foreach (uint ack in PendingAcks.Values)
 | |
|                                 {
 | |
|                                     Pack.Header.AckList[i] = ack;
 | |
|                                     i++;
 | |
|                                 }
 | |
| 
 | |
|                                 PendingAcks.Clear();
 | |
|                                 Pack.Header.AppendedAcks = true;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             byte[] ZeroOutBuffer = new byte[4096];
 | |
|             byte[] sendbuffer;
 | |
|             sendbuffer = Pack.ToBytes();
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 if (Pack.Header.Zerocoded)
 | |
|                 {
 | |
|                     int packetsize = Helpers.ZeroEncode(sendbuffer, sendbuffer.Length, ZeroOutBuffer);
 | |
|                     m_application.SendPacketTo(ZeroOutBuffer, packetsize, SocketFlags.None, CircuitCode);//userEP);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     m_application.SendPacketTo(sendbuffer, sendbuffer.Length, SocketFlags.None, CircuitCode); //userEP);
 | |
|                 }
 | |
|             }
 | |
|             catch (Exception)
 | |
|             {
 | |
|                 OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + simEP.ToString() + " - killing thread");
 | |
|                 ClientThread.Abort();
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         public virtual void InPacket(Packet NewPack)
 | |
|         {
 | |
|             // Handle appended ACKs
 | |
|             if (NewPack.Header.AppendedAcks)
 | |
|             {
 | |
|                 lock (NeedAck)
 | |
|                 {
 | |
|                     foreach (uint ack in NewPack.Header.AckList)
 | |
|                     {
 | |
|                         NeedAck.Remove(ack);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Handle PacketAck packets
 | |
|             if (NewPack.Type == PacketType.PacketAck)
 | |
|             {
 | |
|                 PacketAckPacket ackPacket = (PacketAckPacket)NewPack;
 | |
| 
 | |
|                 lock (NeedAck)
 | |
|                 {
 | |
|                     foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
 | |
|                     {
 | |
|                         NeedAck.Remove(block.ID);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if ((NewPack.Type == PacketType.StartPingCheck))
 | |
|             {
 | |
|                 //reply to pingcheck
 | |
|                 libsecondlife.Packets.StartPingCheckPacket startPing = (libsecondlife.Packets.StartPingCheckPacket)NewPack;
 | |
|                 libsecondlife.Packets.CompletePingCheckPacket endPing = new CompletePingCheckPacket();
 | |
|                 endPing.PingID.PingID = startPing.PingID.PingID;
 | |
|                 OutPacket(endPing);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 QueItem item = new QueItem();
 | |
|                 item.Packet = NewPack;
 | |
|                 item.Incoming = true;
 | |
|                 this.PacketQueue.Enqueue(item);
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         public virtual void OutPacket(Packet NewPack)
 | |
|         {
 | |
|             QueItem item = new QueItem();
 | |
|             item.Packet = NewPack;
 | |
|             item.Incoming = false;
 | |
|             this.PacketQueue.Enqueue(item);
 | |
|         }
 | |
| 
 | |
|         protected virtual void ClientLoop()
 | |
|         {
 | |
|             OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs:ClientLoop() - Entered loop");
 | |
|             while (true)
 | |
|             {
 | |
|                 QueItem nextPacket = PacketQueue.Dequeue();
 | |
|                 if (nextPacket.Incoming)
 | |
|                 {
 | |
|                     //is a incoming packet
 | |
|                     ProcessInPacket(nextPacket.Packet);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     //is a out going packet
 | |
|                     ProcessOutPacket(nextPacket.Packet);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
| }
 |