blob: 437d51832f9d53a77c3462c708f0064a4465fdaa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.apache.directory.server.dhcp.service;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Iterator;
import org.apache.directory.server.dhcp.DhcpException;
import org.apache.directory.server.dhcp.messages.DhcpMessage;
import org.apache.directory.server.dhcp.options.DhcpOption;
import org.apache.directory.server.dhcp.options.OptionsField;
import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract implementation of the server-side DHCP protocol. This class just
* provides some utility methods and dispatches server-bound messages to handler
* methods which can be overridden to provide the functionality.
* <p>
* Client-bound messages and BOOTP messages are ignored.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*
*/
public abstract class AbstractDhcpService implements DhcpService
{
private static final Logger logger = LoggerFactory
.getLogger( AbstractDhcpService.class );
/*
* @see org.apache.directory.server.dhcp.DhcpService#getReplyFor(org.apache.directory.server.dhcp.messages.DhcpMessage)
*/
public final DhcpMessage getReplyFor( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
// ignore messages with an op != REQUEST/REPLY
if ( ( request.getOp() != DhcpMessage.OP_BOOTREQUEST )
&& ( request.getOp() != DhcpMessage.OP_BOOTREPLY ) )
{
return null;
}
// message type option MUST be set - we don't support plain BOOTP.
if ( null == request.getMessageType() )
{
logger.warn( "Missing message type option - plain BOOTP not supported." );
return null;
}
// dispatch based on the message type
switch ( request.getMessageType() )
{
// client-to-server messages
case DHCPDISCOVER:
return handleDISCOVER( localAddress, clientAddress, request );
case DHCPREQUEST:
return handleREQUEST( localAddress, clientAddress, request );
case DHCPRELEASE:
return handleRELEASE( localAddress, clientAddress, request );
case DHCPINFORM:
return handleINFORM( localAddress, clientAddress, request );
case DHCPOFFER:
return handleOFFER( localAddress, clientAddress, request );
// server-to-client messages
case DHCPDECLINE:
case DHCPACK:
case DHCPNAK:
return null; // just ignore them
default:
return handleUnknownMessage( clientAddress, request );
}
}
/**
* Handle unknown DHCP message. The default implementation just logs and
* ignores it.
*
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
*/
protected DhcpMessage handleUnknownMessage( InetSocketAddress clientAddress,
DhcpMessage request )
{
if ( logger.isWarnEnabled() )
{
logger.warn( "Got unknkown DHCP message: {} from: {}", request, clientAddress );
}
return null;
}
/**
* Handle DHCPINFORM message. The default implementation just ignores it.
*
* @param localAddress
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
*/
protected DhcpMessage handleINFORM( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Got INFORM message: {} from: {}", request, clientAddress );
}
return null;
}
/**
* Handle DHCPRELEASE message. The default implementation just ignores it.
*
* @param localAddress
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
*/
protected DhcpMessage handleRELEASE( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
if ( logger.isDebugEnabled() )
logger.debug( "Got RELEASE message: {} from: {}", request, clientAddress );
return null;
}
/**
* Handle DHCPREQUEST message. The default implementation just ignores it.
*
* @param localAddress
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
*/
protected DhcpMessage handleREQUEST( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Got REQUEST message: {} from: {}", request, clientAddress );
}
return null;
}
/**
* Handle DHCPDISCOVER message. The default implementation just ignores it.
*
* @param localAddress
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
* @throws DhcpException
*/
protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Got DISCOVER message: {} from: {}", request, clientAddress );
}
return null;
}
/**
* Handle DHCPOFFER message. The default implementation just ignores it.
*
* @param localAddress
* @param clientAddress
* @param request the request message
* @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
* it.
* @throws DhcpException
*/
protected DhcpMessage handleOFFER( InetSocketAddress localAddress,
InetSocketAddress clientAddress, DhcpMessage request )
throws DhcpException
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Got OFFER message: {} from: {}", request, clientAddress );
}
return null;
}
/**
* Initialize a general DHCP reply message. Sets:
* <ul>
* <li>op=BOOTREPLY
* <li>htype, hlen, xid, flags, giaddr, chaddr like in request message
* <li>hops, secs to 0.
* <li>server hostname to the hostname appropriate for the interface the
* request was received on
* <li>the server identifier set to the address of the interface the request
* was received on
* </ul>
*
* @param localAddress
* @param request
* @return DhcpMessage
*/
protected final DhcpMessage initGeneralReply( InetSocketAddress localAddress,
DhcpMessage request )
{
DhcpMessage reply = new DhcpMessage();
reply.setOp( DhcpMessage.OP_BOOTREPLY );
reply.setHardwareAddress( request.getHardwareAddress() );
reply.setTransactionId( request.getTransactionId() );
reply.setFlags( request.getFlags() );
reply.setRelayAgentAddress( request.getRelayAgentAddress() );
// set server hostname
reply.setServerHostname( localAddress.getHostName() );
// set server identifier based on the IF on which we received the packet
reply.getOptions().add( new ServerIdentifier( localAddress.getAddress() ) );
return reply;
}
/**
* Check if an address is the zero-address
*
* @param addr
* @return boolean
*/
private boolean isZeroAddress( byte[] addr )
{
for ( int i = 0; i < addr.length; i++ )
{
if ( addr[i] != 0 )
{
return false;
}
}
return true;
}
/**
* Determine address on which to base selection. If the relay agent address is
* set, we use the relay agent's address, otherwise we use the address we
* received the request from.
*
* @param clientAddress
* @param request
* @return InetAddress
*/
protected final InetAddress determineSelectionBase(
InetSocketAddress clientAddress, DhcpMessage request )
{
// FIXME: do we know
// a) the interface address over which we received a message (!)
// b) the client address (if specified)
// c) the relay agent address?
// if the relay agent address is set, we use it as the selection base
if ( !isZeroAddress( request.getRelayAgentAddress().getAddress() ) )
{
return request.getRelayAgentAddress();
}
return clientAddress.getAddress();
}
/**
* Strip options that the client doesn't want, if the ParameterRequestList
* option is present.
*
* @param request
* @param options
*/
protected final void stripUnwantedOptions( DhcpMessage request,
OptionsField options )
{
ParameterRequestList prl = ( ParameterRequestList ) request
.getOptions().get( ParameterRequestList.class );
if ( null != prl )
{
byte[] list = prl.getData();
for ( Iterator i = options.iterator(); i.hasNext(); )
{
DhcpOption o = ( DhcpOption ) i.next();
boolean found = false;
for ( int j = 0; j < list.length; j++ )
{
if ( list[j] == o.getTag() )
{
found = true;
break;
}
}
if ( !found )
{
i.remove();
}
}
}
}
}