* close two potential race conditions where a new asynchronous UDP recieve could overwrite an existing endpoint that had not yet been used by the previous thread

* in practice these race conditions were probably pretty rare
0.6.0-stable
Justin Clarke Casey 2008-10-17 20:14:31 +00:00
parent 5f4cad62c5
commit 90d69a0523
1 changed files with 53 additions and 69 deletions

View File

@ -58,10 +58,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected byte[] ZeroBuffer = new byte[8192]; protected byte[] ZeroBuffer = new byte[8192];
/// <value> /// <value>
/// The endpoint of a sender of a particular packet. The port is changed by the various socket receive methods /// This is an endpoint that is reused where we don't need to protect the information from potentially
/// being stomped on by other threads.
/// </value> /// </value>
protected EndPoint reusedEpSender = new IPEndPoint(IPAddress.Any, 0); protected EndPoint reusedEpSender = new IPEndPoint(IPAddress.Any, 0);
protected EndPoint reusedEpProxy;
protected int proxyPortOffset; protected int proxyPortOffset;
protected AsyncCallback ReceivedData; protected AsyncCallback ReceivedData;
@ -178,11 +179,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Packet packet = null; Packet packet = null;
int numBytes = 1; int numBytes = 1;
bool ok = false; bool ok = false;
reusedEpSender = new IPEndPoint(IPAddress.Any, 0); EndPoint epSender = new IPEndPoint(IPAddress.Any, 0);
try try
{ {
numBytes = m_socket.EndReceiveFrom(result, ref reusedEpSender); numBytes = m_socket.EndReceiveFrom(result, ref epSender);
ok = true; ok = true;
} }
catch (SocketException e) catch (SocketException e)
@ -218,12 +219,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (z = numBytes ; z < RecvBuffer.Length ; z++) for (z = numBytes ; z < RecvBuffer.Length ; z++)
RecvBuffer[z] = 0; RecvBuffer[z] = 0;
reusedEpProxy = reusedEpSender;
if (proxyPortOffset != 0)
{
reusedEpSender = ProxyCodec.DecodeProxyMessage(RecvBuffer, ref numBytes);
}
int packetEnd = numBytes - 1; int packetEnd = numBytes - 1;
try try
@ -244,28 +239,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
EndPoint epProxy = null;
// If we've received a use circuit packet, then we need to decode an endpoint proxy, if one exists, before
// allowing the RecvBuffer to be overwritten by the next packet.
if (packet.Type == PacketType.UseCircuitCode)
{
epProxy = epSender;
if (proxyPortOffset != 0)
{
epSender = ProxyCodec.DecodeProxyMessage(RecvBuffer, ref numBytes);
}
}
BeginReceive(); BeginReceive();
if (packet != null) if (packet != null)
ProcessInPacket(packet); {
if (packet.Type == PacketType.UseCircuitCode)
AddNewClient((UseCircuitCodePacket)packet, epSender, epProxy);
else
ProcessInPacket(packet, epSender);
}
} }
/// <summary> /// <summary>
/// Process a successfully received packet. We pass the packet on to the LLPacketServer /// Process a successfully received packet.
/// except in the case that the packet is UseCircuitCode. In that case we set up the circuit code instead.
/// </summary> /// </summary>
/// <param name="packet"></param> /// <param name="packet"></param>
protected virtual void ProcessInPacket(Packet packet) /// <param name="epSender"></param>
protected virtual void ProcessInPacket(Packet packet, EndPoint epSender)
{ {
try try
{ {
// do we already have a circuit for this endpoint // do we already have a circuit for this endpoint
uint circuit; uint circuit;
bool ret; bool ret;
lock (clientCircuits) lock (clientCircuits)
{ {
ret = clientCircuits.TryGetValue(reusedEpSender, out circuit); ret = clientCircuits.TryGetValue(epSender, out circuit);
} }
if (ret) if (ret)
@ -275,21 +288,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_packetServer.InPacket(circuit, packet); m_packetServer.InPacket(circuit, packet);
} }
else if (packet.Type == PacketType.UseCircuitCode)
{
UseCircuitCodePacket p = (UseCircuitCodePacket)packet;
AddNewClient(p);
// Ack the first UseCircuitCode packet
PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
// TODO: don't create new blocks if recycling an old packet
ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
ack_it.Packets[0].ID = packet.Header.Sequence;
ack_it.Header.Reliable = false;
SendPacketTo(ack_it.ToBytes(), ack_it.ToBytes().Length, SocketFlags.None, p.CircuitCode.Code);
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -319,7 +317,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
catch (Exception a) catch (Exception a)
{ {
m_log.Info("[UDPSERVER]: " + a); m_log.Error("[UDPSERVER]: " + a);
} }
try try
@ -356,61 +354,47 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Add a new client circuit. /// Add a new client circuit.
/// </summary> /// </summary>
/// <param name="packet"></param> /// <param name="packet"></param>
protected virtual void AddNewClient(UseCircuitCodePacket useCircuit) /// <param name="epSender"></param>
/// <param name="epProxy"></param>
protected virtual void AddNewClient(UseCircuitCodePacket useCircuit, EndPoint epSender, EndPoint epProxy)
{ {
//Slave regions don't accept new clients //Slave regions don't accept new clients
if (m_localScene.Region_Status != RegionStatus.SlaveScene) if (m_localScene.Region_Status != RegionStatus.SlaveScene)
{
lock (clientCircuits)
{
if (!clientCircuits.ContainsKey(epSender))
{ {
m_log.DebugFormat( m_log.DebugFormat(
"[CLIENT]: Adding new circuit for agent {0}, circuit code {1}", "[CLIENT]: Adding new circuit for agent {0}, circuit code {1}",
useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code); useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code);
// Copy the current reusedEpSender and reusedEpProxy to store in the maps and hashes,
// since the reused ones will change on the next packet receipt.
IPEndPoint reusedIpEpSender = (IPEndPoint)reusedEpSender;
EndPoint epSender = new IPEndPoint(reusedIpEpSender.Address, reusedIpEpSender.Port);
IPEndPoint reusedIpEpPorxy = (IPEndPoint)reusedEpProxy;
EndPoint epProxy = new IPEndPoint(reusedIpEpPorxy.Address, reusedIpEpPorxy.Port);
lock (clientCircuits)
{
if (!clientCircuits.ContainsKey(epSender))
{
clientCircuits.Add(epSender, useCircuit.CircuitCode.Code); clientCircuits.Add(epSender, useCircuit.CircuitCode.Code);
} }
else
{
m_log.Error(
"[CLIENT]: clientCircuits already contains entry for user "
+ useCircuit.CircuitCode.Code + ". NOT adding.");
}
} }
// This doesn't need locking as it's synchronized data // This doesn't need locking as it's synchronized data
if (!clientCircuits_reverse.ContainsKey(useCircuit.CircuitCode.Code)) if (!clientCircuits_reverse.ContainsKey(useCircuit.CircuitCode.Code))
clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, epSender); clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, epSender);
else
m_log.Error(
"[CLIENT]: clientCurcuits_reverse already contains entry for user "
+ useCircuit.CircuitCode.Code + ". NOT adding.");
lock (proxyCircuits) lock (proxyCircuits)
{ {
if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code)) if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code))
proxyCircuits.Add(useCircuit.CircuitCode.Code, epProxy); proxyCircuits.Add(useCircuit.CircuitCode.Code, epProxy);
else
m_log.Error(
"[CLIENT]: proxyCircuits already contains entry for user "
+ useCircuit.CircuitCode.Code + ". NOT adding.");
} }
if (!PacketServer.AddNewClient(epSender, useCircuit, m_assetCache, m_circuitManager, epProxy)) PacketServer.AddNewClient(epSender, useCircuit, m_assetCache, m_circuitManager, epProxy);
m_log.ErrorFormat(
"[CLIENT]: A circuit already existed for agent {0}, circuit {1}",
useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code);
} }
// Ack the UseCircuitCode packet
PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
// TODO: don't create new blocks if recycling an old packet
ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
ack_it.Packets[0].ID = useCircuit.Header.Sequence;
ack_it.Header.Reliable = false;
SendPacketTo(ack_it.ToBytes(), ack_it.ToBytes().Length, SocketFlags.None, useCircuit.CircuitCode.Code);
PacketPool.Instance.ReturnPacket(useCircuit); PacketPool.Instance.ReturnPacket(useCircuit);
} }