/** * Implements the EdgeListener function to * create edges of this type. */ public override void CreateEdgeTo(TransportAddress ta, EdgeCreationCallback ecb) { Edge e = null; Exception ex = null; if (!IsStarted) { ex = new EdgeException("UdpEdgeListener is not started"); } else if (ta.TransportAddressType != this.TAType) { ex = new EdgeException(ta.TransportAddressType.ToString() + " is not my type: " + this.TAType.ToString()); } else if (_ta_auth.Authorize(ta) == TAAuthorizer.Decision.Deny) { ex = new EdgeException(ta.ToString() + " is not authorized"); } else { IPAddress first_ip = ((IPTransportAddress)ta).GetIPAddress(); IPEndPoint end = new IPEndPoint(first_ip, ((IPTransportAddress)ta).Port); /* We have to keep our mapping of end point to edges up to date */ lock ( _id_ht ) { //Get a random ID for this edge: int id; do { id = _rand.Next(); //Make sure we don't have negative ids if (id < 0) { id = ~id; } } while(_id_ht.Contains(id) || id == 0); e = new UdpEdge(this, false, end, _local_ep, id, 0); _id_ht[id] = e; } NatDataPoint dp = new NewEdgePoint(DateTime.UtcNow, e); Interlocked.Exchange <NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange <IEnumerable>(ref _nat_tas, new NatTAs(_tas, _nat_hist)); try { /* Tell me when you close so I can clean up the table */ e.CloseEvent += this.CloseHandler; } catch (Exception x) { e = null; ex = x; } } if (e != null) { ecb(true, e, null); } else { ecb(false, null, ex); } }
protected void SendControlPacket(EndPoint end, int remoteid, int localid, ControlCode c, object state) { using (MemoryStream ms = new MemoryStream()) { NumberSerializer.WriteInt((int)c, ms); if (c == ControlCode.EdgeDataAnnounce) { UdpEdge e = (UdpEdge)_id_ht[localid]; if ((e != null) && (e.RemoteID == remoteid)) { Hashtable t = new Hashtable(); t["RemoteTA"] = e.RemoteTA.ToString(); t["LocalTA"] = e.LocalTA.ToString(); AdrConverter.Serialize(t, ms); } else { if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Problem sending EdgeData: EndPoint: {0}, remoteid: {1}, " + "localid: {2}, Edge: {3}", end, remoteid, localid, e)); } } } _send_queue.Enqueue(new UdpMessage(localid, ~remoteid, MemBlock.Reference(ms.ToArray()), end)); if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Sending control {1} to: {0}", end, c)); } } }
/** * When a new Connection is added, we may need to update the list * of TAs to make sure it is not too long, and that the it is sorted * from most likely to least likely to be successful * @param e the new Edge * @param ta the TransportAddress our TA according to our peer */ public override void UpdateLocalTAs(Edge e, TransportAddress ta) { if (e.TAType == this.TAType) { UdpEdge ue = (UdpEdge)e; ue.PeerViewOfLocalTA = ta; NatDataPoint dp = new LocalMappingChangePoint(DateTime.UtcNow, e, ta); Interlocked.Exchange <NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange <IEnumerable>(ref _nat_tas, new NatTAs(_tas, _nat_hist)); } }
/** * When UdpEdge objects call Send, it calls this packet callback: */ public void HandleEdgeSend(Edge from, ICopyable p) { if (_send_queue.Count > 1024) { Console.WriteLine("Send queue too big: " + _send_queue.Count); // This may be causing the memory leak ... not certain return; // throw new EdgeException(true, "Could not send on: " + from); } UdpEdge edge = from as UdpEdge; _send_queue.Enqueue(new UdpMessage(edge.ID, edge.RemoteID, p, edge.End)); }
/** * When UdpEdge objects call Send, it calls this packet * callback: * @todo The previous interface did not throw an exception to a user * since the send was called in another thread. All code that calls this * could be updated to handle exceptions that the socket might throw. */ public void HandleEdgeSend(Edge from, ICopyable p) { UdpEdge sender = (UdpEdge)from; lock (_send_sync) { //Write the IDs of the edge: //[local id 4 bytes][remote id 4 bytes][packet] NumberSerializer.WriteInt(sender.ID, _send_buffer, 0); NumberSerializer.WriteInt(sender.RemoteID, _send_buffer, 4); int plength = p.CopyTo(_send_buffer, 8); try { _s.SendTo(_send_buffer, 8 + plength, SocketFlags.None, sender.End); } catch (Exception x) { bool transient = (1 == _running); throw new SendException(transient, String.Format("Problem sending on: {0}", sender), x); } } }
/** * When a UdpEdge closes we need to remove it from * our table, so we will know it is new if it comes * back. */ public void CloseHandler(object edge, EventArgs args) { UdpEdge e = (UdpEdge)edge; lock ( _id_ht ) { if (_id_ht.Contains(e.ID)) { _id_ht.Remove(e.ID); object re = _remote_id_ht[e.RemoteID]; if (re == e) { //_remote_id_ht only keeps track of incoming edges, //so, there could be two edges with the same remoteid //that are not equivalent. _remote_id_ht.Remove(e.RemoteID); } NatDataPoint dp = new EdgeClosePoint(DateTime.UtcNow, e); Interlocked.Exchange <NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange <IEnumerable>(ref _nat_tas, new NatTAs(_tas, _nat_hist)); } } }
/** * Implements the EdgeListener function to * create edges of this type. */ public override void CreateEdgeTo(TransportAddress ta, EdgeCreationCallback ecb) { Edge e = null; Exception ex = null; if( !IsStarted ) { ex = new EdgeException("UdpEdgeListener is not started"); } else if( ta.TransportAddressType != this.TAType ) { ex = new EdgeException(ta.TransportAddressType.ToString() + " is not my type: " + this.TAType.ToString() ); } else if( _ta_auth.Authorize(ta) == TAAuthorizer.Decision.Deny ) { ex = new EdgeException( ta.ToString() + " is not authorized"); } else { IPAddress first_ip = ((IPTransportAddress) ta).GetIPAddress(); IPEndPoint end = new IPEndPoint(first_ip, ((IPTransportAddress) ta).Port); /* We have to keep our mapping of end point to edges up to date */ lock( _id_ht ) { //Get a random ID for this edge: int id; do { id = _rand.Next(); //Make sure we don't have negative ids if( id < 0 ) { id = ~id; } } while( _id_ht.Contains(id) || id == 0 ); e = new UdpEdge(this, false, end, _local_ep, id, 0); _id_ht[id] = e; } NatDataPoint dp = new NewEdgePoint(DateTime.UtcNow, e); Interlocked.Exchange<NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange<IEnumerable>(ref _nat_tas, new NatTAs( _tas, _nat_hist )); try { /* Tell me when you close so I can clean up the table */ e.CloseEvent += this.CloseHandler; } catch (Exception x) { e = null; ex = x; } } if(e != null) { ecb(true, e, null); } else { ecb(false, null, ex); } }
/** * This reads a packet from buf which came from end, with * the given ids */ protected void HandleDataPacket(int remoteid, int localid, MemBlock packet, EndPoint end, object state) { bool read_packet = true; bool is_new_edge = false; //It is threadsafe to read from Hashtable UdpEdge edge = (UdpEdge)_id_ht[localid]; if( localid == 0 ) { //This is a potentially a new incoming edge is_new_edge = true; //Check to see if it is a dup: UdpEdge e_dup = (UdpEdge)_remote_id_ht[remoteid]; if( e_dup != null ) { //Lets check to see if this is a true dup: if( e_dup.End.Equals( end ) ) { //Same id from the same endpoint, looks like a dup... is_new_edge = false; //Reuse the existing edge: edge = e_dup; } else { //This is just a coincidence. } } if( is_new_edge ) { TransportAddress rta = TransportAddressFactory.CreateInstance(this.TAType,(IPEndPoint)end); if( _ta_auth.Authorize(rta) == TAAuthorizer.Decision.Deny ) { //This is bad news... Ignore it... ///@todo perhaps we should send a control message... I don't know is_new_edge= false; read_packet = false; if(ProtocolLog.UdpEdge.Enabled) ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Denying: {0}", rta)); } else { //We need to assign it a local ID: lock( _id_ht ) { /* * Now we need to lock the table so that it cannot * be written to by anyone else while we work */ do { localid = _rand.Next(); //Make sure not to use negative ids if( localid < 0 ) { localid = ~localid; } } while( _id_ht.Contains(localid) || localid == 0 ); /* * We copy the endpoint because (I think) .Net * overwrites it each time. Since making new * edges is rare, this is better than allocating * a new endpoint each time */ IPEndPoint this_end = (IPEndPoint)end; IPEndPoint my_end = new IPEndPoint(this_end.Address, this_end.Port); edge = new UdpEdge(_send_handler, true, my_end, _local_ep, localid, remoteid); _id_ht[localid] = edge; _remote_id_ht[remoteid] = edge; } } } } else if ( edge == null ) { /* * This is the case where the Edge is not a new edge, * but we don't know about it. It is probably an old edge * that we have closed. We can ignore this packet */ read_packet = false; //Send a control packet SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); } else if ( edge.RemoteID == 0 ) { /* This is the response to our edge creation */ edge.RemoteID = remoteid; } else if( edge.RemoteID != remoteid ) { /* * This could happen as a result of packet loss or duplication * on the first packet. We should ignore any packet that * does not have both ids matching. */ read_packet = false; //Tell the other guy to close this ignored edge SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); edge = null; } if( (edge != null) && !edge.End.Equals(end) ) { //This happens when a NAT mapping changes if(ProtocolLog.UdpEdge.Enabled) ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Remote NAT Mapping changed on Edge: {0}\n{1} -> {2}", edge, edge.End, end)); //Actually update: TransportAddress rta = TransportAddressFactory.CreateInstance(this.TAType,(IPEndPoint)end); if( _ta_auth.Authorize(rta) != TAAuthorizer.Decision.Deny ) { IPEndPoint this_end = (IPEndPoint) end; edge.End = new IPEndPoint(this_end.Address, this_end.Port); NatDataPoint dp = new RemoteMappingChangePoint(DateTime.UtcNow, edge); Interlocked.Exchange<NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange<IEnumerable>(ref _nat_tas, new NatTAs( _tas, _nat_hist )); //Tell the other guy: SendControlPacket(end, remoteid, localid, ControlCode.EdgeDataAnnounce, state); } else { /* * Looks like the new TA is no longer authorized. */ SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); RequestClose(edge); CloseHandler(edge, null); } } if( is_new_edge ) { try { NatDataPoint dp = new NewEdgePoint(DateTime.UtcNow, edge); Interlocked.Exchange<NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange<IEnumerable>(ref _nat_tas, new NatTAs( _tas, _nat_hist )); edge.CloseEvent += this.CloseHandler; //If we make it here, the edge wasn't closed, go ahead and process it. SendEdgeEvent(edge); // Stun support SendControlPacket(end, remoteid, localid, ControlCode.EdgeDataAnnounce, state); } catch { //Make sure this edge is closed and we are done with it. RequestClose(edge); CloseHandler(edge, null); read_packet = false; //This was a new edge, so the other node has our id as zero, send //with that localid: SendControlPacket(end, remoteid, 0, ControlCode.EdgeClosed, state); } } if( read_packet ) { //We have the edge, now tell the edge to announce the packet: try { edge.ReceivedPacketEvent(packet); } catch(EdgeClosedException) { SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); //Make sure we record that this edge has been closed CloseHandler(edge, null); } } }
/** * This reads a packet from buf which came from end, with * the given ids */ protected void HandleDataPacket(int remoteid, int localid, MemBlock packet, EndPoint end, object state) { bool read_packet = true; bool is_new_edge = false; //It is threadsafe to read from Hashtable UdpEdge edge = (UdpEdge)_id_ht[localid]; if (localid == 0) { //This is a potentially a new incoming edge is_new_edge = true; //Check to see if it is a dup: UdpEdge e_dup = (UdpEdge)_remote_id_ht[remoteid]; if (e_dup != null) { //Lets check to see if this is a true dup: if (e_dup.End.Equals(end)) { //Same id from the same endpoint, looks like a dup... is_new_edge = false; //Reuse the existing edge: edge = e_dup; } else { //This is just a coincidence. } } if (is_new_edge) { TransportAddress rta = TransportAddressFactory.CreateInstance(this.TAType, (IPEndPoint)end); if (_ta_auth.Authorize(rta) == TAAuthorizer.Decision.Deny) { //This is bad news... Ignore it... ///@todo perhaps we should send a control message... I don't know is_new_edge = false; read_packet = false; if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Denying: {0}", rta)); } } else { //We need to assign it a local ID: lock ( _id_ht ) { /* * Now we need to lock the table so that it cannot * be written to by anyone else while we work */ do { localid = _rand.Next(); //Make sure not to use negative ids if (localid < 0) { localid = ~localid; } } while(_id_ht.Contains(localid) || localid == 0); /* * We copy the endpoint because (I think) .Net * overwrites it each time. Since making new * edges is rare, this is better than allocating * a new endpoint each time */ IPEndPoint this_end = (IPEndPoint)end; IPEndPoint my_end = new IPEndPoint(this_end.Address, this_end.Port); edge = new UdpEdge(_send_handler, true, my_end, _local_ep, localid, remoteid); _id_ht[localid] = edge; _remote_id_ht[remoteid] = edge; } } } } else if (edge == null) { /* * This is the case where the Edge is not a new edge, * but we don't know about it. It is probably an old edge * that we have closed. We can ignore this packet */ read_packet = false; //Send a control packet SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); } else if (edge.RemoteID == 0) { /* This is the response to our edge creation */ edge.RemoteID = remoteid; } else if (edge.RemoteID != remoteid) { /* * This could happen as a result of packet loss or duplication * on the first packet. We should ignore any packet that * does not have both ids matching. */ read_packet = false; //Tell the other guy to close this ignored edge SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); edge = null; } if ((edge != null) && !edge.End.Equals(end)) { //This happens when a NAT mapping changes if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Remote NAT Mapping changed on Edge: {0}\n{1} -> {2}", edge, edge.End, end)); } //Actually update: TransportAddress rta = TransportAddressFactory.CreateInstance(this.TAType, (IPEndPoint)end); if (_ta_auth.Authorize(rta) != TAAuthorizer.Decision.Deny) { IPEndPoint this_end = (IPEndPoint)end; edge.End = new IPEndPoint(this_end.Address, this_end.Port); NatDataPoint dp = new RemoteMappingChangePoint(DateTime.UtcNow, edge); Interlocked.Exchange <NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange <IEnumerable>(ref _nat_tas, new NatTAs(_tas, _nat_hist)); //Tell the other guy: SendControlPacket(end, remoteid, localid, ControlCode.EdgeDataAnnounce, state); } else { /* * Looks like the new TA is no longer authorized. */ SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); RequestClose(edge); CloseHandler(edge, null); } } if (is_new_edge) { try { NatDataPoint dp = new NewEdgePoint(DateTime.UtcNow, edge); Interlocked.Exchange <NatHistory>(ref _nat_hist, _nat_hist + dp); Interlocked.Exchange <IEnumerable>(ref _nat_tas, new NatTAs(_tas, _nat_hist)); edge.CloseEvent += this.CloseHandler; //If we make it here, the edge wasn't closed, go ahead and process it. SendEdgeEvent(edge); // Stun support SendControlPacket(end, remoteid, localid, ControlCode.EdgeDataAnnounce, state); } catch { //Make sure this edge is closed and we are done with it. RequestClose(edge); CloseHandler(edge, null); read_packet = false; //This was a new edge, so the other node has our id as zero, send //with that localid: SendControlPacket(end, remoteid, 0, ControlCode.EdgeClosed, state); } } if (read_packet) { //We have the edge, now tell the edge to announce the packet: try { edge.ReceivedPacketEvent(packet); } catch (EdgeClosedException) { SendControlPacket(end, remoteid, localid, ControlCode.EdgeClosed, state); //Make sure we record that this edge has been closed CloseHandler(edge, null); } } }
/** * This handles lightweight control messages that may be sent * by UDP */ protected void HandleControlPacket(int remoteid, int n_localid, MemBlock buffer, object state) { int local_id = ~n_localid; UdpEdge e = _id_ht[local_id] as UdpEdge; if (e == null) { return; } if (e.RemoteID == 0) { try { e.RemoteID = remoteid; } catch { return; } } if (e.RemoteID != remoteid) { return; } try { ControlCode code = (ControlCode)NumberSerializer.ReadInt(buffer, 0); if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Got control {1} from: {0}", e, code)); } if (code == ControlCode.EdgeClosed) { //The edge has been closed on the other side RequestClose(e); CloseHandler(e, null); } else if (code == ControlCode.EdgeDataAnnounce) { //our NAT mapping may have changed: IDictionary info = (IDictionary)AdrConverter.Deserialize(buffer.Slice(4)); string our_local_ta = (string)info["RemoteTA"]; //his remote is our local if (our_local_ta != null) { //Update our list: TransportAddress new_ta = TransportAddressFactory.CreateInstance(our_local_ta); TransportAddress old_ta = e.PeerViewOfLocalTA; if (!new_ta.Equals(old_ta)) { if (ProtocolLog.UdpEdge.Enabled) { ProtocolLog.Write(ProtocolLog.UdpEdge, String.Format( "Local NAT Mapping changed on Edge: {0}\n{1} => {2}", e, old_ta, new_ta)); } //Looks like matters have changed: this.UpdateLocalTAs(e, new_ta); /** * @todo, maybe we should ping the other edges sharing this * EndPoint, but we need to be careful not to do some O(E^2) * operation, which could easily happen if each EdgeDataAnnounce * triggered E packets to be sent */ } } } else if (code == ControlCode.Null) { //Do nothing in this case } } catch (Exception x) { //This could happen if this is some control message we don't understand if (ProtocolLog.Exceptions.Enabled) { ProtocolLog.Write(ProtocolLog.Exceptions, x.ToString()); } } }