/// <summary> /// Removes a <see cref="DynDnsHostEntry" /> registration from the Dynamic /// DNS cluster. /// </summary> /// <param name="hostEntry">The host/IP address <see cref="DynDnsHostEntry" /> to be unregistered.</param> /// <remarks> /// <note> /// This method does not throw an exception if the registration is not present. /// </note> /// </remarks> /// <exception cref="InvalidOperationException">Thrown if the client has not been started.</exception> public void Unregister(DynDnsHostEntry hostEntry) { using (TimedLock.Lock(syncLock)) { if (hosts.ContainsKey(hostEntry)) { // For UDP mode, we need to send two immediate Unregister message to each DNS // server for the host entry. if (settings.Mode == DynDnsMode.Udp && isOpen) { for (int i = 0; i < 2; i++) { DynDnsMessage message; byte[] packet; message = new DynDnsMessage(DynDnsMessageFlag.OpUnregister, hostEntry); packet = message.ToArray(settings.SharedKey); foreach (var nameServer in settings.NameServers) { socket.SendTo(packet, nameServer); } } } // Remove the host entry from the local table. hosts.Remove(hostEntry); Register(); } } }
/// <summary> /// Completes a pending asynchronous socket connection attempt. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" /> instance returned by the initiating call to <see cref="BeginConnect" />.</param> /// <exception cref="SocketException">Thrown if the connection could not be establised.</exception> /// <remarks> /// <note> /// All successful calls to <see cref="BeginConnect" /> should be eventually matched with a call to <see cref="EndConnect" />. /// </note> /// </remarks> public void EndConnect(IAsyncResult ar) { lock (syncLock) { if (isTcp) { sock.EndConnect(ar); } else { var addresses = Dns.EndGetHostAddresses(ar); sock.Bind(); udpRemoteEndPoint = new IPEndPoint(addresses[0], remoteBinding.Port); isUdpConnected = true; // $hack(jeff.lill) // // This is a minor hack. Instead of adding the additional complexity of transmitting the // connection packet asynchronously, I'm just going to make a synchronous call here. This // shouldn't ever block in real life since the socket send buffer starts out empty. sock.SendTo(udpConnectPacket, udpRemoteEndPoint); udpConnectPacket = null; // Don't need this any longer } } }
/// <summary> /// Stops the server, removing it from the cluster. /// </summary> /// <remarks> /// <note> /// It is not an error to call this method then the instance has already been closed. /// </note> /// </remarks> public void Close() { lock (syncLock) { if (closePending || socket == null) { return; } var packet = GetMessageBytes(UdpBroadcastMessageType.ServerUnregister); foreach (var server in settings.Servers) { if (!PauseNetwork) { socket.SendTo(packet, server); } } closePending = true; } // Sleep for a couple seconds so that any broadcast messages in transit // will still be retransmitted during the time it will take for the // other servers in the cluster to decide on a new master. Thread.Sleep(2000); socket.Close(); socket = null; if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } servers.Clear(); clients.Clear(); GC.SuppressFinalize(this); }
/// <summary> /// Initiates an asynchronous operation to authenticate user credentials. /// </summary> /// <param name="realm">Specifies the authentication scope.</param> /// <param name="account">The account.</param> /// <param name="password">The password.</param> /// <param name="callback">The delegate to be called when the operation completes (or <c>null</c>).</param> /// <param name="state">Application defined state (or <c>null</c>).</param> /// <returns>The <see cref="IAsyncResult" /> instance to be used to track the operation.</returns> /// <remarks> /// <note> /// All successful calls to <see cref="BeginAuthenticate" /> must eventually be /// matched by a call to <see cref="EndAuthenticate" />. /// </note> /// </remarks> public IAsyncResult BeginAuthenticate(string realm, string account, string password, AsyncCallback callback, object state) { AsyncResult arAuth = new AsyncResult(null, callback, state); AuthTransaction transaction; RadiusPacket packet; string userName; int ID; IPEndPoint binding; using (TimedLock.Lock(this)) { userName = Helper.GetUserName(realmFormat, realm, account); ID = GetTransactionID(); if (ID == -1) { throw new RadiusException("RADIUS client cannot track more than 256 simultaneous authentication requests."); } binding = GetServerBinding(ref serverPos); if (binding == NetworkBinding.Any) { throw new RadiusException("None of the RADIUS server hosts resolve to an IP address."); } packet = new RadiusPacket(RadiusCode.AccessRequest, ID, Crypto.Rand(16)); packet.Attributes.Add(new RadiusAttribute(RadiusAttributeType.UserName, userName)); packet.Attributes.Add(new RadiusAttribute(RadiusAttributeType.UserPassword, packet.EncryptUserPassword(password, secret))); packet.Attributes.Add(new RadiusAttribute(RadiusAttributeType.NasIpAddress, nasIPAddress)); transaction = new AuthTransaction(userName, packet, serverPos, SysTime.Now + retryInterval, arAuth);; transactions[ID] = transaction; sock.SendTo(transaction.Packet.ToArray(), binding); arAuth.Result = null; arAuth.Started(); } return(arAuth); }
/// <summary> /// Closes the instance, removing it from the broadcast group. /// </summary> /// <remarks> /// <note> /// It is not an error to call this method then the instance has already been closed. /// </note> /// </remarks> public void Close() { lock (syncLock) { if (socket != null) { var packet = GetMessageBytes(UdpBroadcastMessageType.ClientUnregister, settings.BroadcastGroup); foreach (var server in servers) { if (!PauseNetwork) { try { socket.SendTo(packet, server); } catch (Exception e) { SysLog.LogException(e); } } } socket.Close(); socket = null; } if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } } GC.SuppressFinalize(this); }
/// <summary> /// Asynchronously transmits the message passed to the destination /// indicated by the <see paramref="remoteEP" /> parameter. /// </summary> /// <param name="remoteEP">The destination SIP endpoint's <see cref="NetworkBinding" />.</param> /// <param name="message">The <see cref="SipMessage" /> to be transmitted.</param> /// <exception cref="SipTransportException">Thrown if the remote endpoint rejected the message or timed out.</exception> public void Send(NetworkBinding remoteEP, SipMessage message) { if (disabled) { return; } if ((traceMode & SipTraceMode.Send) != 0) { SipHelper.Trace(string.Format("UDP: sending to {0}", remoteEP), message); } try { sock.SendTo(message.ToArray(), remoteEP); } catch (SocketException e) { // $todo(jeff.lill): // // This is just copied from the TCP transport. It probably // doesn't apply here. switch ((SocketError)e.ErrorCode) { case SocketError.ConnectionAborted: case SocketError.ConnectionRefused: case SocketError.ConnectionReset: case SocketError.HostDown: case SocketError.HostNotFound: case SocketError.HostUnreachable: throw new SipTransportException(SipTransportException.ErrorType.Rejected, e.Message, e); case SocketError.TimedOut: throw new SipTransportException(SipTransportException.ErrorType.Timeout, e.Message, e); default: throw; } } }
/// <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); } }
private static int Reflector(string portArg) { try { NetReflector reflector; int port; if (!int.TryParse(portArg, out port)) { Program.Error("Invalid network port."); return(1); } Console.WriteLine(); Console.WriteLine("Starting network reflector on port [{0}]", port); Console.WriteLine("Press [C] to close all connections and [X] to exit the test."); Console.WriteLine(); reflector = new NetReflector(port); while (true) { var ch = Console.ReadKey(true); switch (ch.KeyChar) { case 'c': case 'C': reflector.CloseConnections(); break; case 'x': case 'X': reflector.Stop(); return(0); #if TEST // UDP test code case 'u': case 'U': { var sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); var buf = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var ep = (EndPoint) new IPEndPoint(IPAddress.Any, 0); sock.Bind(); sock.SendTo(buf, 0, buf.Length, SocketFlags.None, new IPEndPoint(IPAddress.Loopback, port)); for (int i = 0; i < buf.Length; i++) { buf[i] = 0; } sock.ReceiveFrom(buf, ref ep); sock.Close(); } break; // TCP test code case 't': case 'T': { var sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sock.NoDelay = true; sock.Connect(new IPEndPoint(IPAddress.Loopback, port)); for (int i = 0; i < 10; i++) { var buf = new byte[i + 1]; for (int j = 0; j < i + 1; j++) { buf[j] = (byte)j; } sock.Send(buf); for (int j = 0; j < i + 1; j++) { buf[j] = 0; } var cbRecv = sock.Receive(buf); } sock.Close(); } break; #endif } } } catch (Exception e) { Program.Error("Error ({0}): {1}", e.GetType().Name, e.Message); return(1); } }