/// <summary> /// Process DECLINE message type. /// </summary> private void DhcpDecline(DhcpMessageEventArgs args) { #region Pre-Processing // Start pre-process validation if (args.RequestOptions.AddressRequest == null) { Logger.WriteDebug(this, "Ignoring DECLINE message: " + "Requested IP option is null"); return; // if processin should not continue } // End pre-Process validation #endregion Pre-Processing if (args.RequestOptions.AddressRequest != null) { args.ResponseBinding.Address = new InternetAddress(args.RequestOptions.AddressRequest); } BindingLease lease = _bindmgr.SetDeclined(args.ResponseBinding); // no reply for v4 decline if (lease == null) { Logger.WriteDebug(this, "DECLINE message: " + "No binding available for client to decline"); } else { this.OnLeaseDeclined(this, new DhcpLeaseEventArgs(lease.Address, lease)); } }
/// <summary> /// Process RELEASE message type. /// </summary> private void DhcpRelease(DhcpMessageEventArgs args) { #region Pre-Processing // Start pre-process validation if (args.RequestMessage.ClientAddress.Equals(InternetAddress.Any)) { Logger.WriteDebug(this, "Ignoring RELEASE message: " + "ciAddr is zero"); return; // if processing should not continue } // End pre-Process validation #endregion Pre-Processing BindingLease lease = _bindmgr.SetReleased(args.ResponseBinding); // no reply for v4 release if (lease == null) { Logger.WriteDebug(this, "RELEASE message: " + "No binding available for client to release"); } else { this.OnLeaseReleased(this, new DhcpLeaseEventArgs(lease.Address, lease)); } }
/// <summary> /// Set client response lease options. /// </summary> private void SetClientOptions(DhcpMessageEventArgs args) { // set client response options args.ResponseMessage.AddOption(DhcpOption.SubnetMask, this._subnet.ToArray()); args.ResponseMessage.AddOption(DhcpOption.Router, this._gateway.ToArray()); if (!StringUtility.IsNullOrEmpty(this._dnsSuffix)) { args.ResponseMessage.AddOption(DhcpOption.DomainNameSuffix, Encoding.UTF8.GetBytes(this._dnsSuffix)); } if (this._dnsServers != null && this._dnsServers.Count > 0) { byte[] dnsServerAddresses = new byte[this._dnsServers.Count * 4]; for (Int32 i = 0; i < this._dnsServers.Count; i++) { InternetAddress ipAddressValue = (InternetAddress)this._dnsServers[i]; ipAddressValue.ToArray().CopyTo(dnsServerAddresses, i * 4); } args.ResponseMessage.AddOption(DhcpOption.DomainNameServer, dnsServerAddresses); } // set custom response options if (this._options != null && this._options.Count > 0) { foreach (DictionaryEntry option in _options) { args.ResponseMessage.AddOption((DhcpOption)option.Key, (byte[])option.Value); } } }
/// <summary> /// Send NAK message back to remote client. /// </summary> private void SendNak(DhcpMessageEventArgs args) { // Set options args.ResponseMessage.AddOption(DhcpOption.DhcpMessageType, (Byte)MessageType.Nak); Logger.WriteInfo(this, "NAK (Negative Acknowledge) for message id " + args.RequestMessage.SessionId.ToHexString("0x") + " sent on thread(#" + Thread.CurrentThread.ManagedThreadId + ")" + " with option message: " + ByteUtility.GetSafeString(args.ResponseMessage.GetOptionData(DhcpOption.DhcpMessage))); this.SendReply(args); }
/// <summary> /// Send ACK message back to remote client. /// </summary> private void SendAck(DhcpMessageEventArgs args, BindingLease lease) { // set header args.ResponseMessage.AssignedAddress = lease.Address; // set lease options this.SetLeaseOptions(args); this.SendAck(args); }
/// <summary> /// Send OFFER message back to remote client. /// </summary> private void SendOffer(DhcpMessageEventArgs args, BindingLease offer) { // set header args.ResponseMessage.AssignedAddress = offer.Address; args.ResponseMessage.NextServerAddress = args.ResponseMessage.NextServerAddress; args.ResponseMessage.AddOption(DhcpOption.DhcpMessageType, (Byte)MessageType.Offer); this.SetLeaseOptions(args); this.SetClientOptions(args); byte[] paramList = args.RequestOptions.ParameterList; if (paramList != null) { args.ResponseMessage.OptionOrdering = paramList; } Logger.WriteInfo(this, "OFFER for message id " + args.RequestMessage.SessionId.ToHexString("0x") + " sent on thread(#" + Thread.CurrentThread.ManagedThreadId + ")"); this.SendReply(args); }
/// <summary> /// Send ACK message back to remote client. /// </summary> private void SendAck(DhcpMessageEventArgs args) { // set header args.ResponseMessage.ClientAddress = args.RequestMessage.ClientAddress; args.ResponseMessage.NextServerAddress = args.RequestMessage.NextServerAddress; // set client options args.ResponseMessage.AddOption(DhcpOption.DhcpMessageType, (Byte)MessageType.Ack); this.SetClientOptions(args); byte[] paramList = args.RequestOptions.ParameterList; if (paramList != null) { args.ResponseMessage.OptionOrdering = paramList; } Logger.WriteInfo(this, "ACK (Acknowledge) for message id " + args.RequestMessage.SessionId.ToHexString("0x") + " sent on thread(#" + Thread.CurrentThread.ManagedThreadId + ")"); this.SendReply(args); }
/// <summary> /// Remote client connects and makes a request. /// </summary> private void OnClientConnect(object sender, ClientConnectedEventArgs args) { SocketBuffer channelBuffer = args.ChannelBuffer; if (channelBuffer != null && args.Channel.IsConnected && channelBuffer.BytesTransferred >= Constants.DHCP_MIN_MESSAGE_SIZE && channelBuffer.BytesTransferred <= Constants.DHCP_MAX_MESSAGE_SIZE) { Logger.WriteDebug(this, "PACKET request with channel id " + args.Channel.ChannelId.ToString() + " was received from " + args.Channel.RemoteEndpoint.ToString() + " and queued for processing..."); DhcpMessageEventArgs messageArgs = new DhcpMessageEventArgs(args.Channel, args.ChannelBuffer); OnDhcpMessageReceived(this, messageArgs); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessRequest), messageArgs); } }
/// <summary> /// Process INFORM message type. /// </summary> private void DhcpInform(DhcpMessageEventArgs args) { #region Pre-Processing // Start pre-process validation // if the client provided a ServerID option, then it MUST // match our configured ServerID, otherwise ignore the request InternetAddress serverIdentifier = new InternetAddress(args.RequestOptions.ServerIdentifier); //InternetAddress localServerIdentifier = new InternetAddress(((IPEndPoint)_dhcpSocket.LocalEndPoint).Address.GetAddressBytes()); InternetAddress localServerIdentifier = new InternetAddress(this.InterfaceAddress); if (!serverIdentifier.Equals(localServerIdentifier)) { Logger.WriteDebug(this, "Ignoring INFORM message: " + "Requested ServerId: " + serverIdentifier.ToString() + " Local ServerId: " + localServerIdentifier.ToString()); return; // if processing should not continue } // End pre-Process validation #endregion Pre-Processing this.SendAck(args); }
/// <summary> /// Process DISCOVERY message type. /// </summary> private void DhcpDiscover(DhcpMessageEventArgs args) { #region Pre-Processing // Start pre-process validation if (!args.RequestMessage.ClientAddress.Equals(InternetAddress.Any)) { Logger.WriteDebug(this, "Ignoring DISCOVER message: ciAddr field is non-zero: " + args.RequestMessage.ClientAddress.ToString()); return; // if processing not should continue } if (args.RequestOptions.ServerIdentifier != null) { Logger.WriteDebug(this, "Ignoring DISCOVER message: ServerId option is not null"); return; // if processing should not continue } // End pre-Process validation #endregion Pre-Processing if (args.ResponseBinding.Address.Equals(InternetAddress.Any) && args.RequestOptions.AddressRequest != null) { args.ResponseBinding.Address = new InternetAddress(args.RequestOptions.AddressRequest); } BindingLease offer = _bindmgr.GetOffer(args.ResponseBinding); if (offer == null) { Logger.WriteDebug(this, "DISCOVER message: " + "No binding available for client"); } else { this.SendOffer(args, offer); } }
/// <summary> /// Process REQUEST message type. /// </summary> private void DhcpRequest(DhcpMessageEventArgs args) { RequestState requestState; #region Pre-Processing // Start pre-process validation //--------------------------------------------------------------------- //| |INIT-REBOOT |SELECTING |RENEWING |REBINDING | //--------------------------------------------------------------------- //|broad/unicast |broadcast |broadcast |unicast |broadcast | //|server-ip |MUST NOT |MUST |MUST NOT |MUST NOT | //|requested-ip |MUST |MUST |MUST NOT |MUST NOT | //|ciaddr |zero |zero |IP address |IP address| //--------------------------------------------------------------------- // first determine what KIND of request we are dealing with if (args.RequestMessage.ClientAddress.Equals(InternetAddress.Any)) { // the ciAddr MUST be 0.0.0.0 for Init-Reboot and Selecting if (args.RequestOptions.AddressRequest == null) { // init-reboot MUST NOT have server-id option requestState = RequestState.InitReboot; } else { // selecting MUST have server-id option requestState = RequestState.Selecting; } } else { // the ciAddr MUST NOT be 0.0.0.0 for Renew and Rebind if (!args.RequestMessage.IsBroadcast) { // renew is unicast // NOTE: this will not happen if the v4 broadcast interface used at startup, // but handling of DHCPv4 renew/rebind is the same anyway requestState = RequestState.Renewing; } else { // rebind is broadcast requestState = RequestState.Rebinding; } } if ((requestState == RequestState.InitReboot) || (requestState == RequestState.Selecting)) { if (args.RequestOptions.AddressRequest == null) { Logger.WriteDebug(this, "Ignoring REQUEST " + RequestStateString.GetName(requestState) + " message: Requested IP option is null"); return; // if processing should not continue } if (requestState == RequestState.Selecting) { // if the client provided a ServerID option, then it MUST // match our configured ServerID, otherwise ignore the request InternetAddress serverIdentifier = new InternetAddress(args.RequestOptions.ServerIdentifier); //InternetAddress localServerIdentifier = new InternetAddress(((IPEndPoint)_dhcpSocket.LocalEndPoint).Address.GetAddressBytes()); InternetAddress localServerIdentifier = new InternetAddress(this.InterfaceAddress); if (!serverIdentifier.Equals(localServerIdentifier)) { Logger.WriteDebug(this, "Ignoring REQUEST " + RequestStateString.GetName(requestState) + " message: " + "Requested ServerId: " + serverIdentifier.ToString() + " Local ServerId: " + localServerIdentifier.ToString()); return; // if processing should not continue } } } else { // type == Renewing or Rebinding if (args.RequestOptions.AddressRequest != null) { Logger.WriteDebug(this, "Ignoring REQUEST " + RequestStateString.GetName(requestState) + " message: " + "Requested IP option is not null"); return; // if processing should not continue } } // End pre-process validation #endregion Pre-Processing Logger.WriteDebug(this, "Processing REQUEST " + RequestStateString.GetName(requestState) + " message"); //TODO: should also check for ip on link if (args.RequestOptions.AddressRequest != null) { args.ResponseBinding.Address = new InternetAddress(args.RequestOptions.AddressRequest); } BindingLease assignment = _bindmgr.GetAssigned(args.ResponseBinding); if (assignment == null) { args.ResponseMessage.AddOption(DhcpOption.DhcpMessage, Encoding.UTF8.GetBytes("No binding available for client")); Logger.WriteDebug(this, "REQUEST " + RequestStateString.GetName(requestState) + " message: No binding available for client sending NAK"); this.SendNak(args); } else { this.SendAck(args, assignment); this.OnLeaseAcknowledged(this, new DhcpLeaseEventArgs(assignment.Address, assignment)); } }
/// <summary> /// Send response message back to remote client. /// </summary> private void SendReply(DhcpMessageEventArgs args) { // set global headers args.ResponseMessage.Operation = OperationCode.BootReply; args.ResponseMessage.Hardware = args.RequestMessage.Hardware; args.ResponseMessage.HardwareAddressLength = args.RequestMessage.HardwareAddressLength; args.ResponseMessage.SessionId = args.RequestMessage.SessionId; args.ResponseMessage.Flags = args.RequestMessage.Flags; args.ResponseMessage.RelayAgentAddress = args.RequestMessage.RelayAgentAddress; args.ResponseMessage.ClientHardwareAddress = args.RequestMessage.ClientHardwareAddress; if (!StringUtility.IsNullOrEmpty(this._serverName)) { args.ResponseMessage.ServerName = Encoding.UTF8.GetBytes(this._serverName); } if (!StringUtility.IsNullOrEmpty(this._bootFileName)) { args.ResponseMessage.BootFileName = Encoding.UTF8.GetBytes(this._bootFileName); } // set global options //args.ResponseMessage.AddOption(DhcpOption.ServerIdentifier, ((IPEndPoint)_dhcpSocket.LocalEndPoint).Address.GetAddressBytes()); args.ResponseMessage.AddOption(DhcpOption.ServerIdentifier, (this.InterfaceAddress.GetAddressBytes())); // determin the proper ip address and port for sendto InternetAddress relayAgentAddress = args.RequestMessage.RelayAgentAddress; InternetAddress clientAddress = args.RequestMessage.ClientAddress; if (!relayAgentAddress.Equals(InternetAddress.Any)) { args.Channel.RemoteEndpoint = new IPEndPoint(relayAgentAddress.ToIPAddress(), Constants.DHCP_SERVICE_PORT); } else { if (!clientAddress.Equals(InternetAddress.Any)) { args.Channel.RemoteEndpoint = new IPEndPoint(clientAddress.ToIPAddress(), Constants.DHCP_CLIENT_PORT); } else { args.Channel.RemoteEndpoint = new IPEndPoint(InternetAddress.Broadcast.ToIPAddress(), Constants.DHCP_CLIENT_PORT); } } try { args.Channel.SendTo(args.ResponseMessage.ToArray(), args.Channel.RemoteEndpoint); Logger.WriteDebug(this, "PACKET with message id " + args.ResponseMessage.SessionId.ToHexString("0x") + " successfully sent to client endpoint " + args.Channel.RemoteEndpoint.ToString()); Logger.WriteDebug(args.ResponseMessage.ToString()); OnDhcpMessageSent(this, args); } catch (SocketException ex) { Logger.WriteError(this, ex.Message + "Socket Error Code: " + ex.ErrorCode.ToString(), ex); } catch (Exception ex) { Logger.WriteError(this, "Error Message:" + ex.Message.ToString(), ex); } }
/// <summary> /// Set global lease options. /// </summary> private void SetLeaseOptions(DhcpMessageEventArgs args) { args.ResponseMessage.AddOption(DhcpOption.AddressTime, ByteUtility.ReverseByteOrder(BitConverter.GetBytes(this.LeaseDuration))); args.ResponseMessage.AddOption(DhcpOption.Renewal, ByteUtility.ReverseByteOrder(BitConverter.GetBytes(this.LeaseRenewal))); args.ResponseMessage.AddOption(DhcpOption.Rebinding, ByteUtility.ReverseByteOrder(BitConverter.GetBytes(this.LeaseRebinding))); }
/// <summary> /// Send response message back to remote client. /// </summary> private void SendReply(DhcpMessageEventArgs args) { // set global headers args.ResponseMessage.Operation = OperationCode.BootReply; args.ResponseMessage.Hardware = args.RequestMessage.Hardware; args.ResponseMessage.HardwareAddressLength = args.RequestMessage.HardwareAddressLength; args.ResponseMessage.SessionId = args.RequestMessage.SessionId; args.ResponseMessage.Flags = args.RequestMessage.Flags; args.ResponseMessage.RelayAgentAddress = args.RequestMessage.RelayAgentAddress; args.ResponseMessage.ClientHardwareAddress = args.RequestMessage.ClientHardwareAddress; if (!StringUtility.IsNullOrEmpty(this._serverName)) args.ResponseMessage.ServerName = Encoding.UTF8.GetBytes(this._serverName); if (!StringUtility.IsNullOrEmpty(this._bootFileName)) args.ResponseMessage.BootFileName = Encoding.UTF8.GetBytes(this._bootFileName); // set global options //args.ResponseMessage.AddOption(DhcpOption.ServerIdentifier, ((IPEndPoint)_dhcpSocket.LocalEndPoint).Address.GetAddressBytes()); args.ResponseMessage.AddOption(DhcpOption.ServerIdentifier, (this.InterfaceAddress.GetAddressBytes())); // determin the proper ip address and port for sendto InternetAddress relayAgentAddress = args.RequestMessage.RelayAgentAddress; InternetAddress clientAddress = args.RequestMessage.ClientAddress; if (!relayAgentAddress.Equals(InternetAddress.Any)) { args.Channel.RemoteEndpoint = new IPEndPoint(relayAgentAddress.ToIPAddress(), Constants.DHCP_SERVICE_PORT); } else { if (!clientAddress.Equals(InternetAddress.Any)) { args.Channel.RemoteEndpoint = new IPEndPoint(clientAddress.ToIPAddress(), Constants.DHCP_CLIENT_PORT); } else { args.Channel.RemoteEndpoint = new IPEndPoint(InternetAddress.Broadcast.ToIPAddress(), Constants.DHCP_CLIENT_PORT); } } try { args.Channel.SendTo(args.ResponseMessage.ToArray(), args.Channel.RemoteEndpoint); Logger.WriteDebug(this, "PACKET with message id " + args.ResponseMessage.SessionId.ToHexString("0x") + " successfully sent to client endpoint " + args.Channel.RemoteEndpoint.ToString()); Logger.WriteDebug(args.ResponseMessage.ToString()); OnDhcpMessageSent(this, args); } catch (SocketException ex) { Logger.WriteError(this, ex.Message + "Socket Error Code: " + ex.ErrorCode.ToString(), ex); } catch (Exception ex) { Logger.WriteError(this, "Error Message:" + ex.Message.ToString(), ex); } }
/// <summary> /// Process boot REQUEST and send reply back to remote client. /// </summary> private void ProcessRequest(Object state) { DhcpMessageEventArgs args = (DhcpMessageEventArgs)state; args.ResponseMessage = new DhcpMessage(); if (args.RequestMessage.Operation == OperationCode.BootRequest) { Byte[] messageTypeData = args.RequestOptions.MessageType; if (messageTypeData != null && messageTypeData.Length == 1) { MessageType messageType = (MessageType)messageTypeData[0]; string requestId = args.RequestMessage.SessionId.ToHexString("0x"); string requestMac = args.RequestMessage.ClientHardwareAddress.ToString(); // classify the client mesage type switch (messageType) { case MessageType.Discover: Logger.WriteInfo(this, "DISCOVER for message id " + requestId + " from client " + requestMac + " processed on thread #" + Thread.CurrentThread.ManagedThreadId); this.DhcpDiscover(args); break; case MessageType.Request: Logger.WriteInfo(this, "REQUEST for message id " + requestId + " from client " + requestMac + " processed on thread #" + Thread.CurrentThread.ManagedThreadId); this.DhcpRequest(args); break; case MessageType.Decline: Logger.WriteInfo(this, "DECLINE for message id " + requestId + " from client " + requestMac + " processed on thread #" + Thread.CurrentThread.ManagedThreadId); this.DhcpDecline(args); break; case MessageType.Release: Logger.WriteInfo(this, "RELEASE for message id " + requestId + " from client " + requestMac + " processed on thread #" + Thread.CurrentThread.ManagedThreadId); this.DhcpRelease(args); break; case MessageType.Inform: Logger.WriteInfo(this, "INFORM for message id " + requestId + " from client " + requestMac + " processed on thread #" + Thread.CurrentThread.ManagedThreadId); this.DhcpInform(args); break; case MessageType.Nak: case MessageType.Ack: case MessageType.Offer: break; default: Logger.WriteInfo(this, "UNKNOWN message (" + messageType.ToString() + ") received and ignored"); break; } } else { Logger.WriteDebug(this, "UNKNOWN message received and ignored"); } } }
static void DhcpService_OnDhcpMessageSent(object sender, DhcpMessageEventArgs args) { Debug.Print("Emulator: DHCP Message Sent " + args.RequestMessage.ToString()); }