/// <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; } }