private bool IsAddressAlreadyAllocated(Lease reservedLease) { foreach (KeyValuePair <ClientIdentifierOption, Lease> lease in _leases) { if (reservedLease.Address.Equals(lease.Value.Address)) { return(!lease.Key.Equals(reservedLease.ClientIdentifier)); } } foreach (KeyValuePair <ClientIdentifierOption, Lease> offer in _offers) { if (reservedLease.Address.Equals(offer.Value.Address)) { return(!offer.Key.Equals(reservedLease.ClientIdentifier)); } } return(false); }
private void UpdateDnsAuthZone(bool add, Scope scope, Lease lease) { if (_authZoneManager == null) { return; } if (string.IsNullOrWhiteSpace(scope.DomainName)) { return; } if (string.IsNullOrWhiteSpace(lease.HostName)) { return; } if (!DnsClient.IsDomainNameValid(lease.HostName)) { return; } if (add) { //update forward zone _authZoneManager.CreatePrimaryZone(scope.DomainName, _authZoneManager.ServerDomain, false); _authZoneManager.SetRecords(lease.HostName, DnsResourceRecordType.A, scope.DnsTtl, new DnsResourceRecordData[] { new DnsARecord(lease.Address) }); //update reverse zone _authZoneManager.CreatePrimaryZone(scope.ReverseZone, _authZoneManager.ServerDomain, false); _authZoneManager.SetRecords(Zone.GetReverseZone(lease.Address, 32), DnsResourceRecordType.PTR, scope.DnsTtl, new DnsResourceRecordData[] { new DnsPTRRecord(lease.HostName) }); } else { //remove from forward zone _authZoneManager.DeleteRecords(lease.HostName, DnsResourceRecordType.A); //remove from reverse zone _authZoneManager.DeleteRecords(Zone.GetReverseZone(lease.Address, 32), DnsResourceRecordType.PTR); } }
private DhcpMessage ProcessDhcpMessage(DhcpMessage request, IPEndPoint remoteEP, IPPacketInformation ipPacketInformation) { if (request.OpCode != DhcpMessageOpCode.BootRequest) { return(null); } switch (request.DhcpMessageType?.Type) { case DhcpMessageType.Discover: { Scope scope = FindScope(request, remoteEP.Address, ipPacketInformation); if (scope == null) { return(null); //no scope available; do nothing } if (scope.OfferDelayTime > 0) { Thread.Sleep(scope.OfferDelayTime); //delay sending offer } Lease offer = scope.GetOffer(request); if (offer == null) { throw new DhcpServerException("DHCP Server failed to offer address: address unavailable due to address pool exhaustion."); } List <DhcpOption> options = scope.GetOptions(request, scope.InterfaceAddress); if (options == null) { return(null); } //log ip offer LogManager log = _log; if (log != null) { log.Write(remoteEP as IPEndPoint, "DHCP Server offered IP address [" + offer.Address.ToString() + "] to " + request.GetClientFullIdentifier() + "."); } return(new DhcpMessage(request, offer.Address, scope.InterfaceAddress, options)); } case DhcpMessageType.Request: { //request ip address lease or extend existing lease Scope scope = FindScope(request, remoteEP.Address, ipPacketInformation); if (scope == null) { return(null); //no scope available; do nothing } Lease leaseOffer; if (request.ServerIdentifier == null) { if (request.RequestedIpAddress == null) { //renewing or rebinding if (request.ClientIpAddress.Equals(IPAddress.Any)) { return(null); //client must set IP address in ciaddr; do nothing } leaseOffer = scope.GetExistingLeaseOrOffer(request); if (leaseOffer == null) { //no existing lease or offer available for client //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } if (!request.ClientIpAddress.Equals(leaseOffer.Address)) { //client ip is incorrect //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } } else { //init-reboot leaseOffer = scope.GetExistingLeaseOrOffer(request); if (leaseOffer == null) { //no existing lease or offer available for client //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } if (!request.RequestedIpAddress.Address.Equals(leaseOffer.Address)) { //the client's notion of its IP address is not correct - RFC 2131 //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } } } else { //selecting offer if (request.RequestedIpAddress == null) { return(null); //client MUST include this option; do nothing } if (!request.ServerIdentifier.Address.Equals(scope.InterfaceAddress)) { return(null); //offer declined by client; do nothing } leaseOffer = scope.GetExistingLeaseOrOffer(request); if (leaseOffer == null) { //no existing lease or offer available for client //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } if (!request.RequestedIpAddress.Address.Equals(leaseOffer.Address)) { //requested ip is incorrect //send nak return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, new DhcpOption[] { new DhcpMessageTypeOption(DhcpMessageType.Nak), new ServerIdentifierOption(scope.InterfaceAddress), DhcpOption.CreateEndOption() })); } } List <DhcpOption> options = scope.GetOptions(request, scope.InterfaceAddress); if (options == null) { return(null); } scope.CommitLease(leaseOffer); //log ip lease LogManager log = _log; if (log != null) { log.Write(remoteEP as IPEndPoint, "DHCP Server leased IP address [" + leaseOffer.Address.ToString() + "] to " + request.GetClientFullIdentifier() + "."); } if (!string.IsNullOrEmpty(scope.DomainName)) { //update dns string clientDomainName = null; foreach (DhcpOption option in options) { if (option.Code == DhcpOptionCode.ClientFullyQualifiedDomainName) { clientDomainName = (option as ClientFullyQualifiedDomainNameOption).DomainName; break; } } if (clientDomainName == null) { if (request.HostName != null) { clientDomainName = request.HostName.HostName + "." + scope.DomainName; } else if ((leaseOffer.Type == LeaseType.Reserved) && !string.IsNullOrEmpty(leaseOffer.HostName) && !leaseOffer.HostName.EndsWith("." + scope.DomainName, StringComparison.OrdinalIgnoreCase)) { clientDomainName = leaseOffer.HostName + "." + scope.DomainName; //use hostname from reserved lease } } if (clientDomainName != null) { leaseOffer.SetHostName(clientDomainName.ToLower()); UpdateDnsAuthZone(true, scope, leaseOffer); } } return(new DhcpMessage(request, leaseOffer.Address, scope.InterfaceAddress, options)); } case DhcpMessageType.Decline: { //ip address is already in use as detected by client via ARP if ((request.ServerIdentifier == null) || (request.RequestedIpAddress == null)) { return(null); //client MUST include these option; do nothing } Scope scope = FindScope(request, remoteEP.Address, ipPacketInformation); if (scope == null) { return(null); //no scope available; do nothing } if (!request.ServerIdentifier.Address.Equals(scope.InterfaceAddress)) { return(null); //request not for this server; do nothing } Lease lease = scope.GetExistingLeaseOrOffer(request); if (lease == null) { return(null); //no existing lease or offer available for client; do nothing } if (!lease.Address.Equals(request.RequestedIpAddress.Address)) { return(null); //the client's notion of its IP address is not correct; do nothing } //remove lease since the IP address is used by someone else scope.ReleaseLease(lease); //log issue LogManager log = _log; if (log != null) { log.Write(remoteEP as IPEndPoint, "DHCP Server received DECLINE message: " + lease.GetClientFullIdentifier() + " detected that IP address [" + lease.Address + "] is already in use."); } //update dns UpdateDnsAuthZone(false, scope, lease); //do nothing return(null); } case DhcpMessageType.Release: { //cancel ip address lease if (request.ServerIdentifier == null) { return(null); //client MUST include this option; do nothing } Scope scope = FindScope(request, remoteEP.Address, ipPacketInformation); if (scope == null) { return(null); //no scope available; do nothing } if (!request.ServerIdentifier.Address.Equals(scope.InterfaceAddress)) { return(null); //request not for this server; do nothing } Lease lease = scope.GetExistingLeaseOrOffer(request); if (lease == null) { return(null); //no existing lease or offer available for client; do nothing } if (!lease.Address.Equals(request.ClientIpAddress)) { return(null); //the client's notion of its IP address is not correct; do nothing } //release lease scope.ReleaseLease(lease); //log ip lease release LogManager log = _log; if (log != null) { log.Write(remoteEP as IPEndPoint, "DHCP Server released IP address [" + lease.Address.ToString() + "] that was leased to " + lease.GetClientFullIdentifier() + "."); } //update dns UpdateDnsAuthZone(false, scope, lease); //do nothing return(null); } case DhcpMessageType.Inform: { //need only local config; already has ip address assigned externally/manually Scope scope = FindScope(request, remoteEP.Address, ipPacketInformation); if (scope == null) { return(null); //no scope available; do nothing } List <DhcpOption> options = scope.GetOptions(request, scope.InterfaceAddress); if (options == null) { return(null); } //log inform LogManager log = _log; if (log != null) { log.Write(remoteEP as IPEndPoint, "DHCP Server received INFORM message from " + request.GetClientFullIdentifier() + "."); } return(new DhcpMessage(request, IPAddress.Any, scope.InterfaceAddress, options)); } default: return(null); } }
public Scope(BinaryReader bR) { if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "SC") { throw new InvalidDataException("DhcpServer scope file format is invalid."); } switch (bR.ReadByte()) { case 1: _name = bR.ReadShortString(); _enabled = bR.ReadBoolean(); ChangeNetwork(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR)); _leaseTimeDays = bR.ReadUInt16(); _leaseTimeHours = bR.ReadByte(); _leaseTimeMinutes = bR.ReadByte(); _offerDelayTime = bR.ReadUInt16(); _domainName = bR.ReadShortString(); if (string.IsNullOrWhiteSpace(_domainName)) { _domainName = null; } _dnsTtl = bR.ReadUInt32(); _routerAddress = IPAddressExtension.Parse(bR); if (_routerAddress.Equals(IPAddress.Any)) { _routerAddress = null; } { int count = bR.ReadByte(); if (count > 0) { if (count == 255) { _useThisDnsServer = true; FindThisDnsServerAddress(); } else { _dnsServers = new IPAddress[count]; for (int i = 0; i < count; i++) { _dnsServers[i] = IPAddressExtension.Parse(bR); } } } } { int count = bR.ReadByte(); if (count > 0) { _winsServers = new IPAddress[count]; for (int i = 0; i < count; i++) { _winsServers[i] = IPAddressExtension.Parse(bR); } } } { int count = bR.ReadByte(); if (count > 0) { _ntpServers = new IPAddress[count]; for (int i = 0; i < count; i++) { _ntpServers[i] = IPAddressExtension.Parse(bR); } } } { int count = bR.ReadByte(); if (count > 0) { _staticRoutes = new ClasslessStaticRouteOption.Route[count]; for (int i = 0; i < count; i++) { _staticRoutes[i] = new ClasslessStaticRouteOption.Route(bR.BaseStream); } } } { int count = bR.ReadByte(); if (count > 0) { _exclusions = new Exclusion[count]; for (int i = 0; i < count; i++) { _exclusions[i] = new Exclusion(IPAddressExtension.Parse(bR), IPAddressExtension.Parse(bR)); } } } { int count = bR.ReadInt32(); if (count > 0) { _reservedLeases = new Lease[count]; for (int i = 0; i < count; i++) { _reservedLeases[i] = new Lease(bR); } } _allowOnlyReservedLeases = bR.ReadBoolean(); } { int count = bR.ReadInt32(); if (count > 0) { for (int i = 0; i < count; i++) { Lease lease = new Lease(bR); _leases.TryAdd(lease.ClientIdentifier, lease); } } } break; default: throw new InvalidDataException("Scope data format version not supported."); } }
internal void ReleaseLease(Lease lease) { _leases.TryRemove(lease.ClientIdentifier, out _); _lastModified = DateTime.UtcNow; }
internal Lease GetOffer(DhcpMessage request) { if (_leases.TryGetValue(request.ClientIdentifier, out Lease existingLease)) { //lease already exists return(existingLease); } if (_reservedLeases != null) { ClientIdentifierOption clientIdentifierKey = new ClientIdentifierOption(1, request.ClientHardwareAddress); foreach (Lease reservedLease in _reservedLeases) { if (reservedLease.ClientIdentifier.Equals(clientIdentifierKey)) { //reserved address exists IPAddress reservedLeaseAddress = reservedLease.Address; if (!IsAddressAvailable(ref reservedLeaseAddress)) { break; //reserved lease address is already allocated so ignore reserved lease } Lease reservedOffer = new Lease(LeaseType.Reserved, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, reservedLease.Address, null, GetLeaseTime()); return(_offers.AddOrUpdate(request.ClientIdentifier, reservedOffer, delegate(ClientIdentifierOption key, Lease existingValue) { return reservedOffer; })); } } } if (_allowOnlyReservedLeases) { throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": scope allows only reserved lease allocations."); } Lease dummyOffer = new Lease(LeaseType.None, null, null, null, null, null, 0); Lease existingOffer = _offers.GetOrAdd(request.ClientIdentifier, dummyOffer); if (dummyOffer != existingOffer) { if (existingOffer.Type == LeaseType.None) { return(null); //dummy offer so another thread is handling offer; do nothing } //offer already exists existingOffer.ExtendLease(GetLeaseTime()); return(existingOffer); } //find offer ip address IPAddress offerAddress = null; if (request.RequestedIpAddress != null) { //client wish to get this address IPAddress requestedAddress = request.RequestedIpAddress.Address; if (IsAddressInRange(requestedAddress) && IsAddressAvailable(ref requestedAddress)) { offerAddress = requestedAddress; } } if (offerAddress == null) { lock (_lastAddressOfferedLock) { //find free address from scope offerAddress = _lastAddressOffered; uint endingAddressNumber = _endingAddress.ConvertIpToNumber(); bool offerAddressWasResetFromEnd = false; while (true) { uint nextOfferAddressNumber = offerAddress.ConvertIpToNumber() + 1u; if (nextOfferAddressNumber > endingAddressNumber) { if (offerAddressWasResetFromEnd) { throw new DhcpServerException("DHCP Server failed to offer IP address to " + request.GetClientFullIdentifier() + ": address unavailable due to address pool exhaustion."); } offerAddress = IPAddressExtension.ConvertNumberToIp(_startingAddress.ConvertIpToNumber() - 1u); offerAddressWasResetFromEnd = true; continue; } offerAddress = IPAddressExtension.ConvertNumberToIp(nextOfferAddressNumber); if (IsAddressAvailable(ref offerAddress)) { break; } } _lastAddressOffered = offerAddress; } } Lease offerLease = new Lease(LeaseType.Dynamic, request.ClientIdentifier, request.HostName?.HostName, request.ClientHardwareAddress, offerAddress, null, GetLeaseTime()); return(_offers.AddOrUpdate(request.ClientIdentifier, offerLease, delegate(ClientIdentifierOption key, Lease existingValue) { return offerLease; })); }
private void UpdateDnsAuthZone(bool add, Scope scope, Lease lease) { UpdateDnsAuthZone(add, scope, lease.HostName, lease.Address); }
private void UpdateDnsAuthZone(bool add, Scope scope, Lease lease) { if (_authoritativeZoneRoot == null) { return; } if (string.IsNullOrWhiteSpace(scope.DomainName)) { return; } if (string.IsNullOrWhiteSpace(lease.HostName)) { return; } if (!DnsClient.IsDomainNameValid(lease.HostName)) { return; } if (add) { //update forward zone { if (!_authoritativeZoneRoot.ZoneExists(scope.DomainName)) { //create forward zone _authoritativeZoneRoot.SetRecords(scope.DomainName, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.DomainName, 1, 14400, 3600, 604800, 900) }); _authoritativeZoneRoot.SetRecords(scope.DomainName, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_authoritativeZoneRoot.ServerDomain) }); _authoritativeZoneRoot.MakeZoneInternal(scope.DomainName); } _authoritativeZoneRoot.SetRecords(lease.HostName, DnsResourceRecordType.A, scope.DnsTtl, new DnsResourceRecordData[] { new DnsARecord(lease.Address) }); } //update reverse zone { if (!_authoritativeZoneRoot.ZoneExists(scope.ReverseZone)) { //create reverse zone _authoritativeZoneRoot.SetRecords(scope.ReverseZone, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.ReverseZone, 1, 14400, 3600, 604800, 900) }); _authoritativeZoneRoot.SetRecords(scope.ReverseZone, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_authoritativeZoneRoot.ServerDomain) }); _authoritativeZoneRoot.MakeZoneInternal(scope.ReverseZone); } _authoritativeZoneRoot.SetRecords(Scope.GetReverseZone(lease.Address, 32), DnsResourceRecordType.PTR, scope.DnsTtl, new DnsResourceRecordData[] { new DnsPTRRecord(lease.HostName) }); } } else { //remove from forward zone _authoritativeZoneRoot.DeleteRecords(lease.HostName, DnsResourceRecordType.A); //remove from reverse zone _authoritativeZoneRoot.DeleteRecords(Scope.GetReverseZone(lease.Address, 32), DnsResourceRecordType.PTR); } }