/// <summary> /// Starts the transport. /// </summary> /// <param name="binding">The network binding the transport will use.</param> /// <param name="cbSocketBuffer">Size of the socket's send and receive buffers in bytes.</param> /// <param name="router">The <see cref="ISipMessageRouter" /> instance that will handle the routing of received messages.</param> /// <exception cref="SocketException">Thrown if there's a conflict with the requested and existing socket bindings.</exception> public void Start(NetworkBinding binding, int cbSocketBuffer, ISipMessageRouter router) { try { sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sock.SendBufferSize = cbSocketBuffer; sock.ReceiveBufferSize = cbSocketBuffer; sock.IgnoreUdpConnectionReset = true; sock.Bind(binding); this.localEP = (IPEndPoint)sock.LocalEndPoint; this.onRecv = new AsyncCallback(OnReceive); this.recvBuf = new byte[64 * 1024]; this.recvEP = new IPEndPoint(IPAddress.Any, 0); this.router = router; this.traceMode = SipTraceMode.None; recvPending = true; sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onRecv, null); } catch { if (sock.IsOpen) { sock.Close(); } sock = null; throw; } }
/// <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); } }
public NetReflector(int port) { // Initialize the UDP service. udpSock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpSock.Bind(new IPEndPoint(IPAddress.Any, port)); udpRecvBuf = new byte[bufSize]; udpRemoteEP = new IPEndPoint(IPAddress.Any, 0); onUdpReceive = new AsyncCallback(OnUdpReceive); udpSock.BeginReceiveFrom(udpRecvBuf, 0, udpRecvBuf.Length, SocketFlags.None, ref udpRemoteEP, onUdpReceive, null); // Initialize the TCP service. onTcpReceive = new AsyncCallback(OnTcpReceive); tcpConnections = new List <EnhancedSocket>(); listener = new SocketListener(); listener.SocketAcceptEvent += (s, a) => { lock (syncLock) { try { var sock = (EnhancedSocket)s; var remoteEP = (IPEndPoint)sock.RemoteEndPoint; var prefix = PadPrefix(string.Format("TCP[{0}:{1}]:", remoteEP.Address, remoteEP.Port)); var recvState = new TcpRecvState() { Socket = sock, Buffer = new byte[bufSize] }; if (TcpConnected != null) { TcpConnected(this, new NetReflectorEventArgs() { Endpoint = remoteEP }); } Debug.WriteLine(string.Format("{0} Connect", prefix)); tcpConnections.Add(sock); sock.BeginReceive(recvState.Buffer, 0, recvState.Buffer.Length, SocketFlags.None, onTcpReceive, recvState); } catch (Exception e) { Debug.WriteLine(string.Format("*** {0}: {1}", e.GetType().Name, e.Message)); } } }; listener.Start(new IPEndPoint(IPAddress.Any, port), 100); }
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> /// Opens a RADIUS client port using the <see cref="RadiusClientSettings" /> passed. /// </summary> /// <param name="settings">The client settings.</param> /// <remarks> /// <note> /// All successful calls to <see cref="Open" /> must eventually be matched /// with a call to <see cref="Close" /> so that system resources will be /// released promptly. /// </note> /// </remarks> public void Open(RadiusClientSettings settings) { using (TimedLock.Lock(this)) { if (isOpen) { throw new RadiusException("RADIUS client port is already open."); } sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sock.Bind(settings.NetworkBinding); this.sock.SendBufferSize = settings.SocketBuffer; this.sock.ReceiveBufferSize = settings.SocketBuffer; isOpen = true; networkBinding = settings.NetworkBinding; servers = settings.Servers; secret = settings.Secret; retryInterval = settings.RetryInterval; maxTransmissions = settings.MaxTransmissions; realmFormat = settings.RealmFormat; nextID = 0; serverPos = 0; transactions = new AuthTransaction[256]; recvBuf = new byte[TcpConst.MTU]; sourceEP = new IPEndPoint(IPAddress.Any, 0); onReceive = new AsyncCallback(OnReceive); if (networkBinding.Address.Equals(IPAddress.Any)) { nasIPAddress = NetHelper.GetActiveAdapter(); } else { nasIPAddress = networkBinding.Address; } // Initiate reception of the first RADIUS packet. sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref sourceEP, onReceive, null); } }
/// <summary> /// Starts the DNS server using the settings passed. /// </summary> /// <param name="settings">The <see cref="DnsServerSettings" /> to be used to initialize the server.</param> /// <exception cref="InvalidOperationException">Thrown if the server has already started.</exception> public void Start(DnsServerSettings settings) { lock (syncLock) { if (sock != null) { throw new InvalidOperationException("DNS server has already started."); } sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sock.IgnoreUdpConnectionReset = true; sock.ReceiveBufferSize = sock.ReceiveBufferSize = 1024 * 1024; // $todo(jeff.lill): Hardcoded sock.Bind(settings.NetworkBinding); remoteEP = new IPEndPoint(IPAddress.Any, 0); sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref remoteEP, onReceive, sock); } }
/// <summary> /// Opens a UDP unicast socket. /// </summary> /// <param name="ep">The UDP endpoint to open.</param> /// <remarks> /// <para> /// Pass <b>ep.Address=IPAddress.Any</b> if the channel should be opened on all /// network adapters. /// </para> /// <para> /// Pass <b>ep.Port=0</b> if Windows should assign the socket's port. The /// port assigned can be determined via the <see cref="Port" /> property. /// </para> /// </remarks> public void OpenUnicast(IPEndPoint ep) { using (TimedLock.Lock(router.SyncRoot)) { this.sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); this.sock.Bind(ep); this.transport = Transport.Udp; this.localEP = router.NormalizeEP(new ChannelEP(Transport.Udp, (IPEndPoint)sock.LocalEndPoint)); this.port = localEP.NetEP.Port; this.sendQueue = new PriorityQueue <Msg>(); this.onSend = new AsyncCallback(OnSend); this.sendMsg = null; this.cbSend = 0; this.sendBuf = null; this.msgBuf = new byte[TcpConst.MTU]; this.cloudEP = null; this.multicastInit = false; this.broadcastClient = null; sendQueue.CountLimit = router.UdpMsgQueueCountMax; sendQueue.SizeLimit = router.UdpMsgQueueSizeMax; this.onSocketReceive = new AsyncCallback(OnSocketReceive); this.recvBuf = new byte[TcpConst.MTU]; router.Trace(1, "UDP: OpenUnicast", "localEP=" + localEP.NetEP.ToString(), null); sock.IgnoreUdpConnectionReset = true; sock.SendBufferSize = router.UdpUnicastSockConfig.SendBufferSize; sock.ReceiveBufferSize = router.UdpUnicastSockConfig.ReceiveBufferSize; // Initiate the first async receive operation on this socket sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onSocketReceive, null); // Mark the channel as open. this.isOpen = true; } }
/// <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> /// Creates and starts a UDP broadcast server using the settings passed. /// </summary> /// <param name="settings">The server settings.</param> /// <param name="perfCounters">The application's performance counters (or <c>null</c>).</param> /// <param name="perfPrefix">The string to prefix any performance counter names (or <c>null</c>).</param> /// <remarks> /// <note> /// The <paramref name="perfCounters" /> parameter is type as <see cref="object" /> so that /// applications using this class will not be required to reference the <b>LillTel.Advanced</b> /// assembly. /// </note> /// </remarks> /// <exception cref="ArgumentException">Thrown if the settings passed are not valid.</exception> public UdpBroadcastServer(UdpBroadcastServerSettings settings, object perfCounters, string perfPrefix) { if (settings == null) { throw new ArgumentNullException("settings"); } if (settings.Servers == null || settings.Servers.Length == 0) { throw new ArgumentException("Invalid UDP broadcast server settings: At least one broadcast server endpoint is required."); } if (perfCounters != null && !(perfCounters is PerfCounterSet)) { throw new ArgumentException("Only instances of type [PerfCounterSet] may be passed in the [perfCounters] parameter.", "perfCounters"); } this.startTime = DateTime.UtcNow; this.closePending = false; this.settings = settings; this.perf = new Perf(perfCounters as PerfCounterSet, perfPrefix); bool found = false; for (int i = 0; i < settings.Servers.Length; i++) { var binding = settings.Servers[i]; if (binding == settings.NetworkBinding) { found = true; // I'm going to special case the situation where the network binding address is ANY. // In this case, one of the server endpoints must also include an ANY entry and I'll // fill out with the loop back addres (127.1.0.1). if (binding.IsAnyAddress) { settings.Servers[i] = new NetworkBinding(IPAddress.Loopback, settings.Servers[i].Port); } break; } } if (!found) { if (!settings.NetworkBinding.IsAnyAddress) { throw new ArgumentException("Invalid UDP broadcast server settings: The current server's network binding must also be present in the Servers[] bindings."); } } // Initialize the clients and servers state. clients = new Dictionary <IPEndPoint, ClientState>(); servers = new Dictionary <IPEndPoint, ServerState>(); // Open the socket and start receiving packets. socket = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.IgnoreUdpConnectionReset = true; socket.ReceiveBufferSize = settings.SocketBufferSize; socket.SendBufferSize = settings.SocketBufferSize; socket.Bind(settings.NetworkBinding); onReceive = new AsyncCallback(OnReceive); recvBuf = new byte[TcpConst.MTU]; rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); // Crank up a timer to send the ClientRegister messages to the server cluster. registerTimer = new PolledTimer(settings.ClusterKeepAliveInterval, true); registerTimer.FireNow(); // Make sure that we send registration messages immediately bkTimer = new GatedTimer(new TimerCallback(OnBkTask), null, TimeSpan.Zero, settings.BkTaskInterval); }
/// <summary> /// Handles received packets. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" /> instance.</param> private void OnReceive(IAsyncResult ar) { DnsRequest request = null; int cbRecv; IPEndPoint ep; try { cbRecv = ((EnhancedSocket)ar.AsyncState).EndReceiveFrom(ar, ref remoteEP); } catch { cbRecv = 0; } if (sock == null) { return; // The server has stopped } if (cbRecv != 0) { // Parse the request packet try { request = new DnsRequest(); request.ParsePacket(recvBuf, cbRecv); } catch (Exception e) { SysLog.LogException(e); } } // Save the remote EP and then initiate another async // packet receive. ep = (IPEndPoint)remoteEP; remoteEP = new IPEndPoint(IPAddress.Any, 0); sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref remoteEP, onReceive, sock); // Process the request and transmit the response (if there is one). if (request != null && RequestEvent != null) { var args = new DnsServerEventArgs(ep, request); RequestEvent(this, args); if (args.Response != null) { byte[] sendBuf; int cbSend; // $todo(jeff.lill): // // Remove this exception code after figuring out why the // response's QName field is sometimes NULL. try { sendBuf = args.Response.FormatPacket(out cbSend); } catch { SysLog.LogError("DNS Formatting Error:\r\n\r\n" + args.Request.GetTraceDetails(ep.Address) + "\r\n" + args.Response.GetTraceDetails(ep.Address)); throw; } lock (syncLock) { if (sock != null) { sock.SendTo(sendBuf, cbSend, SocketFlags.None, args.RemoteEP); } } } } }
/// <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> /// Opens a UDP multicast socket. /// </summary> /// <param name="adapter">The network adapter to bind this socket.</param> /// <param name="cloudEP">Specifies the multicast group and port.</param> /// <remarks> /// <para> /// Pass <b>adapter=IPAddress.Any</b> to bind the socket to all available network /// adapters. /// </para> /// <note> /// A valid port and address must be specified in <b>cloudEP</b>. /// </note> /// </remarks> public void OpenMulticast(IPAddress adapter, IPEndPoint cloudEP) { if (cloudEP.Address.Equals(IPAddress.Any)) { throw new MsgException("Invalid multicast address."); } if (cloudEP.Port == 0) { throw new MsgException("Invalid multicast port."); } using (TimedLock.Lock(router.SyncRoot)) { this.sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); this.sock.ReuseAddress = true; this.sock.EnableBroadcast = true; this.sock.MulticastTTL = MulticastTTL; this.sock.MulticastLoopback = true; this.sock.IgnoreUdpConnectionReset = true; this.sock.Bind(new IPEndPoint(adapter, cloudEP.Port)); // The framework throws an exception if there is no connected network connection // when we attempt to add the socket to the multicast group. I'm going to catch // this exception and track that this didn't work and then periodically retry // the operation. try { this.sock.MulticastGroup = cloudEP.Address; this.multicastInit = true; } catch { this.multicastInit = false; } this.transport = Transport.Multicast; this.localEP = router.NormalizeEP(new ChannelEP(Transport.Udp, router.UdpEP)); this.port = cloudEP.Port; this.sendQueue = new PriorityQueue <Msg>(); this.onSend = new AsyncCallback(OnSend); this.sendMsg = null; this.cbSend = 0; this.sendBuf = null; this.msgBuf = new byte[TcpConst.MTU]; this.cloudEP = cloudEP; this.onSocketReceive = new AsyncCallback(OnSocketReceive); this.recvBuf = new byte[TcpConst.MTU]; sendQueue.CountLimit = router.UdpMsgQueueCountMax; sendQueue.SizeLimit = router.UdpMsgQueueSizeMax; router.Trace(1, "UDP: OpenMulticast", string.Format("cloudEP={0} localEP={1} adaptor={2} NIC={3}", cloudEP, localEP.NetEP, adapter, NetHelper.GetNetworkAdapterIndex(adapter)), null); sock.SendBufferSize = router.UdpMulticastSockConfig.SendBufferSize; sock.ReceiveBufferSize = router.UdpMulticastSockConfig.ReceiveBufferSize; // Initiate the first async receive operation on this socket sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onSocketReceive, null); // Mark the channel as open. this.isOpen = true; } }
/// <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)); } }
/// <summary> /// Creates and starts a UDP broadcast client using the settings passed. /// </summary> /// <param name="settings">The client settings.</param> /// <exception cref="ArgumentException">Thrown if the settings passed are not valid.</exception> public UdpBroadcastClient(UdpBroadcastClientSettings settings) { this.settings = settings; if (settings == null) { throw new ArgumentNullException("settings"); } if (settings.Servers == null || settings.Servers.Length == 0) { throw new ArgumentException("Invalid UDP broadcast client settings: At least one broadcast server endpoint is required."); } this.servers = new List <IPEndPoint>(); // $hack(jeff.lill) // // This is a bit of a hack to discover the source IP address to use for this instance. // If the configured NetworkBinding specifies a specific interface, then we'll use // this, otherwise we'll use the IPv4 address for the first active network adaptor we find. // In a perfect world, I'd send a packet to the broadcast servers and have it respond with // the UDP source address it sees that would discover the actual IP address. This hack // should work 99% of the time though. if (!settings.NetworkBinding.Address.Equals(IPAddress.Any)) { this.sourceAddress = settings.NetworkBinding.Address; } else { this.sourceAddress = NetHelper.GetActiveAdapter(); } // Open the socket and start receiving packets. socket = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.IgnoreUdpConnectionReset = true; socket.ReceiveBufferSize = settings.SocketBufferSize; socket.SendBufferSize = settings.SocketBufferSize; socket.Bind(settings.NetworkBinding); onReceive = new AsyncCallback(OnReceive); recvBuf = new byte[8192]; rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); // Crank up the timers. keepAliveTimer = new PolledTimer(settings.KeepAliveInterval, false); keepAliveTimer.FireNow(); serverResolveTimer = new PolledTimer(settings.ServerResolveInterval, false); serverResolveTimer.FireNow(); bkTimer = new GatedTimer(new TimerCallback(OnBkTask), null, TimeSpan.Zero, settings.BkTaskInterval); // Sleep for a couple seconds to allow the server DNS lookups to complete. Thread.Sleep(2000); }