/// <summary> /// Handles message packets received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnSocketReceive(IAsyncResult ar) { int cb; Msg msg = null; byte[] msgBuf; int cbMsg; IPEndPoint fromEP = NetworkBinding.Any; using (TimedLock.Lock(router.SyncRoot)) { if (!isOpen) { return; } try { cb = sock.EndReceiveFrom(ar, ref recvEP); if (cb > 0) { msgBuf = router.DecryptFrame(recvBuf, cb, out cbMsg); msg = Msg.Load(new EnhancedMemoryStream(msgBuf)); fromEP = (IPEndPoint)recvEP; msg._SetFromChannel(new ChannelEP(transport, fromEP)); } } catch (MsgException) { // Ignore messages that can't be parsed } catch (Exception e) { SysLog.LogException(e); } // Initiate the receive of the next message if (sock.IsOpen) { try { sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onSocketReceive, null); } catch (Exception e) { SysLog.LogException(e, "LillTek UDP message channel is no longer able to receive messages."); } } } if (msg != null) { msg._Trace(router, 2, "UDP: Recv", string.Format("From: {0}", fromEP)); msg._Trace(router, 0, "Receive", string.Empty); router.OnReceive(this, msg); } }
private void OnUdpReceive(IAsyncResult ar) { try { var cbRecv = udpSock.EndReceiveFrom(ar, ref udpRemoteEP); var remoteEP = (IPEndPoint)udpRemoteEP; var prefix = PadPrefix(string.Format("UDP[{0}:{1}]:", remoteEP.Address, remoteEP.Port)); lock (syncLock) { Debug.WriteLine(string.Format("{0} Echoing [{1}] bytes", prefix, cbRecv)); } var udpSendBuf = Helper.Extract(udpRecvBuf, 0, cbRecv); udpSock.BeginSendTo(udpSendBuf, 0, cbRecv, SocketFlags.None, remoteEP, ar2 => { try { udpSock.EndSendTo(ar2); } catch (SocketClosedException) { return; } catch { // Ignore } }, null); if (UdpReceived != null) { UdpReceived(this, new NetReflectorEventArgs() { Endpoint = remoteEP, Data = udpSendBuf }); } udpSock.BeginReceiveFrom(udpRecvBuf, 0, udpRecvBuf.Length, SocketFlags.None, ref udpRemoteEP, onUdpReceive, null); } catch (SocketClosedException) { return; } catch (Exception e) { lock (syncLock) { Debug.WriteLine(string.Format("*** {0}: {1}", e.GetType().Name, e.Message)); } } }
/// <summary> /// Handles the asynchronous reception of UDP packets on the socket. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" /> instance.</param> private void OnReceive(IAsyncResult ar) { AuthTransaction transaction; RadiusPacket response; byte[] rawPacket; int cbPacket; // Complete receiving the packet and then initiate reception // of the next packet. Note that I don't need a lock here // because there's never more than one async packet receive // outstanding at a given time. try { cbPacket = sock.EndReceiveFrom(ar, ref sourceEP); rawPacket = (byte[])recvBuf.Clone(); sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref sourceEP, onReceive, null); response = new RadiusPacket((IPEndPoint)sourceEP, rawPacket, cbPacket); } catch (SocketClosedException) { // We'll see this when the RADIUS server instance is closed. // I'm not going to report this to the event log. return; } catch (Exception e) { SysLog.LogException(e); return; } // Map the packet to a transaction and signal that the // authentication transaction is complete. using (TimedLock.Lock(this)) { if (!isOpen) { return; } transaction = transactions[response.Identifier]; if (transaction == null) { return; // The transaction must have been aborted } // Validate the response authenticator, discarding the packet // if it's not valid. if (!response.VerifyResponseAuthenticator(transaction.Packet, secret)) { transaction.AsyncResult.Result = false; } else { // Complete the outstanding transaction if the packet is a // valid answer. switch (response.Code) { case RadiusCode.AccessAccept: transaction.AsyncResult.Result = true; break; case RadiusCode.AccessReject: transaction.AsyncResult.Result = false; break; default: return; // Ignore all other answers } } transaction.AsyncResult.Notify(); transactions[response.Identifier] = null; } }
/// <summary> /// Called when a packet is received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnReceive(IAsyncResult ar) { UdpBroadcastMessage message = null; IPEndPoint recvEP = null; int cbRecv = 0; lock (syncLock) { if (socket == null) { return; // Client is closed } try { // Parse received packet. cbRecv = socket.EndReceiveFrom(ar, ref rawRecvEP); recvEP = (IPEndPoint)rawRecvEP; if (cbRecv == 0) { return; // This happens when we receive an ICMP(connection-reset) from a } // remote host that's actively refusing an earlier packet transmission. // We're just going to ignore this. perf.TotalMessageRate.Increment(); perf.TotalByteRate.IncrementBy(cbRecv); message = new UdpBroadcastMessage(Helper.Extract(recvBuf, 0, cbRecv), settings.SharedKey); // Validate that the message timestamp is reasonable and discard // messages with timestamps from too far in the past or too far // in the future. DateTime now = DateTime.UtcNow; if (!Helper.Within(now, message.TimeStampUtc, settings.MessageTTL)) { SysLog.LogWarning("UDP Broadcast message timestamp out of range. SystemTime={0}, Timestamp={1}, Source={2}, BroadcastGroup={3}", now.ToString("u"), message.TimeStampUtc.ToString("u"), recvEP, message.BroadcastGroup); return; } } catch (Exception e) { SysLog.LogException(e); } finally { // Initiate the next receive. try { rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); } catch (Exception e) { SysLog.LogException(e); } } // Process the message (if any). if (message == null || PauseNetwork) { return; } ServerState server; ClientState client; switch (message.MessageType) { case UdpBroadcastMessageType.ServerRegister: // Add the server to the tracking table if it's not already // present and update its TTD. if (!servers.TryGetValue(recvEP, out server)) { server = new ServerState(recvEP, SysTime.Now + settings.ServerTTL); servers.Add(recvEP, server); } else { server.TTD = SysTime.Now + settings.ServerTTL; } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ServerUnregister: // Remove the server from the tracking table (if present). if (servers.ContainsKey(recvEP)) { servers.Remove(recvEP); } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ClientRegister: // Add the client to the tracking table if it's not already // present and update its TTD. if (!clients.TryGetValue(recvEP, out client)) { client = new ClientState(recvEP, message.BroadcastGroup, SysTime.Now + settings.ServerTTL); clients.Add(recvEP, client); } else { client.TTD = SysTime.Now + settings.ServerTTL; } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ClientUnregister: // Remove the client from the tracking table (if present). if (clients.ContainsKey(recvEP)) { clients.Remove(recvEP); } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.Broadcast: // Transmit the message to all clients the belong to the same broadcast group, // if this is the master server. if (!IsMaster) { return; } var packet = GetMessageBytes(UdpBroadcastMessageType.Broadcast, message.SourceAddress, message.BroadcastGroup, message.Payload); int cDelivered = 0; foreach (var c in clients.Values) { if (c.BroadcastGroup == message.BroadcastGroup) { socket.SendTo(packet, c.EndPoint); cDelivered++; } } perf.BroadcastReceiveByteRate.IncrementBy(cbRecv); perf.BroadcastReceiveMessageRate.Increment(); perf.BroadcastSendByteRate.IncrementBy(cbRecv * packet.Length); perf.BroadcastSendMessageRate.IncrementBy(cDelivered); break; } } }
/// <summary> /// Handle asynchronous packet reception. /// </summary> /// <param name="ar">The operation's <see cref="IAsyncResult" /> instance.</param> private void OnReceive(IAsyncResult ar) { RadiusPacket request = null; ServerState state = null; int cbRecv; using (TimedLock.Lock(this)) { // Finish receiving the next request packet try { cbRecv = sock.EndReceiveFrom(ar, ref remoteEP); } catch (SocketClosedException) { // We'll see this when the RADIUS server instance is closed. // I'm not going to report this to the event log. return; } catch (Exception e) { // I'm going to assume that something really bad has // happened to the socket, log the exception and then // return without initiating another receive. This // effectively stops the server. SysLog.LogException(e); return; } // Parse the request. We're going to initiate the // authentication below, outside of the lock. try { request = new RadiusPacket((IPEndPoint)remoteEP, recvBuf, cbRecv); // Unit tests can use this hook to monitor incoming packets // as well cause them to be ignored. if (DiagnosticHook != null && !DiagnosticHook(this, request)) { request = null; } if (request != null && request.Code == RadiusCode.AccessRequest) { state = new ServerState(this); } else { // Ignore all RADIUS requests except for Access-Request request = null; } } catch (Exception e) { SysLog.LogException(e); } // Initiate reception of the next request try { sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref remoteEP, onReceive, null); } catch (Exception e) { SysLog.LogException(e); } } if (request == null) { return; // We're ignoring the packet } // Validate the packet and the NAS RadiusNasInfo nasInfo; IPAddress nasIPAddress; byte[] nasIdentifier; string userName; string realm; string account; byte[] encryptedPassword; string password; if (!request.GetAttributeAsAddress(RadiusAttributeType.NasIpAddress, out nasIPAddress) && !request.GetAttributeAsBinary(RadiusAttributeType.NasIdentifier, out nasIdentifier)) { // Access-Request packets are required by RFC 2865 to have either a NAS-IP-Address // or a NAS-IP-Identifier attribute. Discard any packets that don't have one // of these. return; } if (!request.GetAttributeAsText(RadiusAttributeType.UserName, out userName) || !request.GetAttributeAsBinary(RadiusAttributeType.UserPassword, out encryptedPassword)) { // The User-Name attribute is required by RFC 2865 and this implementation // requires a User-Password attribute. Ignore packets without these. return; } // Parse the realm and account from the user name Helper.ParseUserName(realmFormat, userName, out realm, out account); // Lookup the NAS shared secret and decrypt the password. nasInfo = GetNasInfo(state, request.SourceEP.Address); if (nasInfo == null) { if (defSecret == null) { // Not being able to find information about a NAS device could // represent a serious security problem or attack so I'm going // to log this. Log(state, false, RadiusLogEntryType.UnknownNas, realm, account, "RADIUS: Unknown NAS device NAS=[{0}].", request.SourceEP); return; } nasInfo = new RadiusNasInfo(request.SourceEP.Address, defSecret); } password = request.DecryptUserPassword(encryptedPassword, nasInfo.Secret); // Perform the authentication, compute the response // authenticator and then send a response packet. RadiusPacket response; if (Authenticate(state, realm, account, password)) { Log(state, true, RadiusLogEntryType.Authentication, realm, account, "Authenticated: realm=[{0}] account=[{1}] NAS=[{2}]", realm, account, request.SourceEP); response = new RadiusPacket(RadiusCode.AccessAccept, request.Identifier, null, new RadiusAttribute(RadiusAttributeType.ServiceType, (int)RadiusServiceType.Login)); } else { Log(state, false, RadiusLogEntryType.Authentication, realm, account, "Authentication Fail: realm=[{0}] account=[{1}] NAS=[{2}]", realm, account, request.SourceEP); response = new RadiusPacket(RadiusCode.AccessReject, request.Identifier, null); } response.ComputeResponseAuthenticator(request, nasInfo.Secret); try { sock.SendTo(response.ToArray(), request.SourceEP); } catch (SocketClosedException) { // We'll see this when the RADIUS server instance is closed. // I'm not going to report this to the event log. } catch (Exception e) { SysLog.LogException(e); } }
/// <summary> /// Called when a packet is received by the socket from a remote endpoint. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" />.</param> private void OnReceive(IAsyncResult ar) { byte[] packet = null; IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int cb; SipMessage message; recvPending = false; if (sock == null || recvBuf == null) { return; } try { cb = sock.EndReceiveFrom(ar, ref recvEP); } catch (Exception e) { // Log the exception if the socket appears to be open and then submit // another receive request. if (sock.IsOpen) { SysLog.LogException(e); try { recvEP = new IPEndPoint(IPAddress.Any, 0); sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onRecv, null); } catch (Exception e2) { SysLog.LogException(e2, "SIP UDP transport is no longer able to receive packets."); } } return; } // $todo(jeff.lill): This is where I need to add source filtering. // Make a copy of what we received before initiating the next packet receive. try { packet = Helper.Extract(recvBuf, 0, cb); remoteEP = (IPEndPoint)recvEP; } catch (Exception e) { SysLog.LogException(e); } // Initiate the receive of the next message lock (syncLock) { if (sock == null || !sock.IsOpen) { return; } try { recvEP = new IPEndPoint(IPAddress.Any, 0); recvPending = true; sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onRecv, null); } catch (Exception e) { SysLog.LogException(e); } } // Parse and dispatch the message if (packet == null) { return; } // It looks like SIP clients like X-Lite send 4 byte CRLF CRLF messages // periodically over UDP to keep NAT mappings alive. I'm going to // ignore these messages. if (packet.Length == 4) { return; } // Looks like we have a real message. try { try { message = SipMessage.Parse(packet, true); } catch (Exception e) { SipHelper.Trace(string.Format("UDP: UNPARSABLE message received from {0}: [{1}]", remoteEP, e.Message), Helper.FromUTF8(packet)); throw; } message.SourceTransport = this; message.RemoteEndpoint = remoteEP; } catch (SipException e) { e.BadPacket = packet; e.SourceEndpoint = remoteEP; SysLog.LogException(e); return; } catch (Exception e) { SipException sipException; sipException = new SipException("Error parsing SIP message.", e); sipException.Transport = string.Format("UDP [{0}]", localEP); sipException.BadPacket = packet; sipException.SourceEndpoint = remoteEP; SysLog.LogException(sipException); return; } if (disabled) { return; } if ((traceMode & SipTraceMode.Receive) != 0) { SipHelper.Trace(string.Format("UDP: received from {0}", remoteEP), message); } router.Route(this, message); }
/// <summary> /// Called when a packet is received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnReceive(IAsyncResult ar) { UdpBroadcastMessage message = null; IPEndPoint recvEP; int cbRecv; lock (syncLock) { if (socket == null) { return; // Client is closed } try { // Parse received packet. cbRecv = socket.EndReceiveFrom(ar, ref rawRecvEP); recvEP = (IPEndPoint)rawRecvEP; if (cbRecv == 0) { return; // This happens when we receive an ICMP(connection-reset) from a } // remote host that's actively refusing an earlier packet transmission. // We're just going to ignore this. message = new UdpBroadcastMessage(Helper.Extract(recvBuf, 0, cbRecv), settings.SharedKey); // Validate that the message timestamp is reasonable and discard // messages with timestamps from too far in the past or too far // in the future. DateTime now = DateTime.UtcNow; if (!Helper.Within(now, message.TimeStampUtc, settings.MessageTTL)) { SysLog.LogWarning("UDP Broadcast message timestamp out of range. SystemTime={0}, Timestamp={1}, Source={2}, BroadcastGroup={3}", now.ToString("u"), message.TimeStampUtc.ToString("u"), recvEP, message.BroadcastGroup); return; } } catch (Exception e) { SysLog.LogException(e); } finally { // Initiate the next receive. try { rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); } catch (Exception e) { SysLog.LogException(e); } } } // Process the message (if any) outside of the lock. Note that only // broadcast messages are processed by UDP broadcast clients. if (message == null || message.MessageType != UdpBroadcastMessageType.Broadcast || PauseNetwork) { return; } // Ignore messages that don't match the broadcast group. if (settings.BroadcastGroup != message.BroadcastGroup) { return; } if (PacketReceived != null) { PacketReceived(this, new UdpBroadcastEventArgs(message.SourceAddress, message.Payload)); } }