View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    * 
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   * 
17   */
18  
19  package org.apache.directory.server.dhcp.service;
20  
21  
22  import java.net.InetAddress;
23  import java.net.InetSocketAddress;
24  import java.util.Iterator;
25  
26  import org.apache.directory.server.dhcp.DhcpException;
27  import org.apache.directory.server.dhcp.messages.DhcpMessage;
28  import org.apache.directory.server.dhcp.options.DhcpOption;
29  import org.apache.directory.server.dhcp.options.OptionsField;
30  import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
31  import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  
36  /**
37   * Abstract implementation of the server-side DHCP protocol. This class just
38   * provides some utility methods and dispatches server-bound messages to handler
39   * methods which can be overridden to provide the functionality.
40   * <p>
41   * Client-bound messages and BOOTP messages are ignored.
42   * 
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   * 
45   */
46  public abstract class AbstractDhcpService implements DhcpService
47  {
48      private static final Logger LOGGER = LoggerFactory.getLogger( AbstractDhcpService.class );
49  
50  
51      /*
52       * @see org.apache.directory.server.dhcp.DhcpService#getReplyFor(org.apache.directory.server.dhcp.messages.DhcpMessage)
53       */
54      public final DhcpMessage getReplyFor( InetSocketAddress localAddress,
55          InetSocketAddress clientAddress, DhcpMessage request )
56          throws DhcpException
57      {
58          // ignore messages with an op != REQUEST/REPLY
59          if ( ( request.getOp() != DhcpMessage.OP_BOOTREQUEST )
60              && ( request.getOp() != DhcpMessage.OP_BOOTREPLY ) )
61          {
62              return null;
63          }
64  
65          // message type option MUST be set - we don't support plain BOOTP.
66          if ( null == request.getMessageType() )
67          {
68              LOGGER.warn( "Missing message type option - plain BOOTP not supported." );
69  
70              return null;
71          }
72  
73          // dispatch based on the message type
74          switch ( request.getMessageType() )
75          {
76          // client-to-server messages
77              case DHCPDISCOVER:
78                  return handleDISCOVER( localAddress, clientAddress, request );
79  
80              case DHCPREQUEST:
81                  return handleREQUEST( localAddress, clientAddress, request );
82  
83              case DHCPRELEASE:
84                  return handleRELEASE( localAddress, clientAddress, request );
85  
86              case DHCPINFORM:
87                  return handleINFORM( localAddress, clientAddress, request );
88  
89              case DHCPOFFER:
90                  return handleOFFER( localAddress, clientAddress, request );
91  
92                  // server-to-client messages
93              case DHCPDECLINE:
94              case DHCPACK:
95              case DHCPNAK:
96                  return null; // just ignore them
97  
98              default:
99                  return handleUnknownMessage( clientAddress, request );
100         }
101     }
102 
103 
104     /**
105      * Handle unknown DHCP message. The default implementation just logs and
106      * ignores it.
107      * 
108      * @param clientAddress
109      * @param request the request message
110      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
111      *         it.
112      */
113     protected DhcpMessage handleUnknownMessage( InetSocketAddress clientAddress,
114         DhcpMessage request )
115     {
116         if ( LOGGER.isWarnEnabled() )
117         {
118             LOGGER.warn( "Got unknkown DHCP message: {} from: {}", request, clientAddress );
119         }
120 
121         return null;
122     }
123 
124 
125     /**
126      * Handle DHCPINFORM message. The default implementation just ignores it.
127      * 
128      * @param localAddress
129      * @param clientAddress
130      * @param request the request message
131      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
132      *         it.
133      */
134     protected DhcpMessage handleINFORM( InetSocketAddress localAddress,
135         InetSocketAddress clientAddress, DhcpMessage request )
136     {
137         if ( LOGGER.isDebugEnabled() )
138         {
139             LOGGER.debug( "Got INFORM message: {} from: {}", request, clientAddress );
140         }
141 
142         return null;
143     }
144 
145 
146     /**
147      * Handle DHCPRELEASE message. The default implementation just ignores it.
148      * 
149      * @param localAddress
150      * @param clientAddress
151      * @param request the request message
152      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
153      *         it.
154      */
155     protected DhcpMessage handleRELEASE( InetSocketAddress localAddress,
156         InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
157     {
158         if ( LOGGER.isDebugEnabled() )
159         {
160             LOGGER.debug( "Got RELEASE message: {} from: {}", request, clientAddress );
161         }
162         return null;
163     }
164 
165 
166     /**
167      * Handle DHCPREQUEST message. The default implementation just ignores it.
168      * 
169      * @param localAddress
170      * @param clientAddress
171      * @param request the request message
172      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
173      *         it.
174      */
175     protected DhcpMessage handleREQUEST( InetSocketAddress localAddress,
176         InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
177     {
178         if ( LOGGER.isDebugEnabled() )
179         {
180             LOGGER.debug( "Got REQUEST message: {} from: {}", request, clientAddress );
181         }
182 
183         return null;
184     }
185 
186 
187     /**
188      * Handle DHCPDISCOVER message. The default implementation just ignores it.
189      * 
190      * @param localAddress
191      * @param clientAddress
192      * @param request the request message
193      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
194      *         it.
195      * @throws DhcpException
196      */
197     protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress,
198         InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
199     {
200         if ( LOGGER.isDebugEnabled() )
201         {
202             LOGGER.debug( "Got DISCOVER message: {} from: {}", request, clientAddress );
203         }
204 
205         return null;
206     }
207 
208 
209     /**
210      * Handle DHCPOFFER message. The default implementation just ignores it.
211      * 
212      * @param localAddress
213      * @param clientAddress
214      * @param request the request message
215      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
216      *         it.
217      * @throws DhcpException
218      */
219     protected DhcpMessage handleOFFER( InetSocketAddress localAddress,
220         InetSocketAddress clientAddress, DhcpMessage request )
221     {
222         if ( LOGGER.isDebugEnabled() )
223         {
224             LOGGER.debug( "Got OFFER message: {} from: {}", request, clientAddress );
225         }
226 
227         return null;
228     }
229 
230 
231     /**
232      * Initialize a general DHCP reply message. Sets:
233      * <ul>
234      * <li>op=BOOTREPLY
235      * <li>htype, hlen, xid, flags, giaddr, chaddr like in request message
236      * <li>hops, secs to 0.
237      * <li>server hostname to the hostname appropriate for the interface the
238      * request was received on
239      * <li>the server identifier set to the address of the interface the request
240      * was received on
241      * </ul>
242      * 
243      * @param localAddress
244      * @param request
245      * @return DhcpMessage
246      */
247     protected final DhcpMessage initGeneralReply( InetSocketAddress localAddress,
248         DhcpMessage request )
249     {
250         DhcpMessager/dhcp/messages/DhcpMessage.html#DhcpMessage">DhcpMessage reply = new DhcpMessage();
251 
252         reply.setOp( DhcpMessage.OP_BOOTREPLY );
253 
254         reply.setHardwareAddress( request.getHardwareAddress() );
255         reply.setTransactionId( request.getTransactionId() );
256         reply.setFlags( request.getFlags() );
257         reply.setRelayAgentAddress( request.getRelayAgentAddress() );
258 
259         // set server hostname
260         reply.setServerHostname( localAddress.getHostName() );
261 
262         // set server identifier based on the IF on which we received the packet
263         reply.getOptions().add( new ServerIdentifier( localAddress.getAddress() ) );
264 
265         return reply;
266     }
267 
268 
269     /**
270      * Check if an address is the zero-address
271      * 
272      * @param addr
273      * @return boolean
274      */
275     private boolean isZeroAddress( byte[] addr )
276     {
277         for ( int i = 0; i < addr.length; i++ )
278         {
279             if ( addr[i] != 0 )
280             {
281                 return false;
282             }
283         }
284 
285         return true;
286     }
287 
288 
289     /**
290      * Determine address on which to base selection. If the relay agent address is
291      * set, we use the relay agent's address, otherwise we use the address we
292      * received the request from.
293      * 
294      * @param clientAddress
295      * @param request
296      * @return InetAddress
297      */
298     protected final InetAddress determineSelectionBase(
299         InetSocketAddress clientAddress, DhcpMessage request )
300     {
301         // FIXME: do we know
302         // a) the interface address over which we received a message (!)
303         // b) the client address (if specified)
304         // c) the relay agent address?
305 
306         // if the relay agent address is set, we use it as the selection base
307         if ( !isZeroAddress( request.getRelayAgentAddress().getAddress() ) )
308         {
309             return request.getRelayAgentAddress();
310         }
311 
312         return clientAddress.getAddress();
313     }
314 
315 
316     /**
317      * Strip options that the client doesn't want, if the ParameterRequestList
318      * option is present.
319      * 
320      * @param request
321      * @param options
322      */
323     protected final void stripUnwantedOptions( DhcpMessage request,
324         OptionsField options )
325     {
326         ParameterRequestList../../org/apache/directory/server/dhcp/options/dhcp/ParameterRequestList.html#ParameterRequestList">ParameterRequestList prl = ( ParameterRequestList ) request
327             .getOptions().get( ParameterRequestList.class );
328 
329         if ( null != prl )
330         {
331             byte[] list = prl.getData();
332 
333             for ( Iterator i = options.iterator(); i.hasNext(); )
334             {
335                 DhcpOption../../../../../../org/apache/directory/server/dhcp/options/DhcpOption.html#DhcpOption">DhcpOption o = ( DhcpOption ) i.next();
336 
337                 boolean found = false;
338 
339                 for ( int j = 0; j < list.length; j++ )
340                 {
341                     if ( list[j] == o.getTag() )
342                     {
343                         found = true;
344                         break;
345                     }
346                 }
347 
348                 if ( !found )
349                 {
350                     i.remove();
351                 }
352             }
353         }
354     }
355 }