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  import java.net.InetAddress;
22  import java.net.InetSocketAddress;
23  import java.util.Iterator;
24  
25  import org.apache.directory.server.dhcp.DhcpException;
26  import org.apache.directory.server.dhcp.messages.DhcpMessage;
27  import org.apache.directory.server.dhcp.messages.MessageType;
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   * Abstract implementation of the server-side DHCP protocol. This class just
37   * provides some utility methods and dispatches server-bound messages to handler
38   * methods which can be overridden to provide the functionality.
39   * <p>
40   * Client-bound messages and BOOTP messages are ignored.
41   * 
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $
44   * 
45   */
46  public abstract class AbstractDhcpService implements DhcpService {
47      private static final Logger logger = LoggerFactory
48              .getLogger(AbstractDhcpService.class);
49  
50      /*
51       * @see org.apache.directory.server.dhcp.DhcpService#getReplyFor(org.apache.directory.server.dhcp.messages.DhcpMessage)
52       */
53      public final DhcpMessage getReplyFor(InetSocketAddress localAddress,
54              InetSocketAddress clientAddress, DhcpMessage request)
55              throws DhcpException {
56          // ignore messages with an op != REQUEST/REPLY
57          if (request.getOp() != DhcpMessage.OP_BOOTREQUEST
58                  && request.getOp() != DhcpMessage.OP_BOOTREPLY)
59              return null;
60  
61          // message type option MUST be set - we don't support plain BOOTP.
62          if (null == request.getMessageType()) {
63              logger.warn("Missing message type option - plain BOOTP not supported.");
64              return null;
65          }
66  
67          // dispatch based on the message type
68          switch (request.getMessageType().getCode()){
69              // client-to-server messages
70              case MessageType.CODE_DHCPDISCOVER :
71                  return handleDISCOVER(localAddress, clientAddress, request);
72              case MessageType.CODE_DHCPREQUEST :
73                  return handleREQUEST(localAddress, clientAddress, request);
74              case MessageType.CODE_DHCPRELEASE :
75                  return handleRELEASE(localAddress, clientAddress, request);
76              case MessageType.CODE_DHCPINFORM :
77                  return handleINFORM(localAddress, clientAddress, request);
78  
79              case MessageType.CODE_DHCPOFFER :
80                  return handleOFFER(localAddress, clientAddress, request);
81  
82                  // server-to-client messages
83              case MessageType.CODE_DHCPDECLINE :
84              case MessageType.CODE_DHCPACK :
85              case MessageType.CODE_DHCPNAK :
86                  return null; // just ignore them
87  
88              default :
89                  return handleUnknownMessage(clientAddress, request);
90          }
91      }
92  
93      /**
94       * Handle unknown DHCP message. The default implementation just logs and
95       * ignores it.
96       * 
97       * @param clientAddress
98       * @param request the request message
99       * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
100      *         it.
101      */
102     protected DhcpMessage handleUnknownMessage(InetSocketAddress clientAddress,
103             DhcpMessage request) {
104         if (logger.isWarnEnabled())
105             logger.warn("Got unknkown DHCP message: " + request + " from:  "
106                     + clientAddress);
107         return null;
108     }
109 
110     /**
111      * Handle DHCPINFORM message. The default implementation just ignores it.
112      * 
113      * @param localAddress
114      * @param clientAddress
115      * @param request the request message
116      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
117      *         it.
118      */
119     protected DhcpMessage handleINFORM(InetSocketAddress localAddress,
120             InetSocketAddress clientAddress, DhcpMessage request)
121             throws DhcpException {
122         if (logger.isDebugEnabled())
123             logger.debug("Got INFORM message: " + request + " from:  "
124                     + clientAddress);
125         return null;
126     }
127 
128     /**
129      * Handle DHCPRELEASE message. The default implementation just ignores it.
130      * 
131      * @param localAddress
132      * @param clientAddress
133      * @param request the request message
134      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
135      *         it.
136      */
137     protected DhcpMessage handleRELEASE(InetSocketAddress localAddress,
138             InetSocketAddress clientAddress, DhcpMessage request)
139             throws DhcpException {
140         if (logger.isDebugEnabled())
141             logger.debug("Got RELEASE message: " + request + " from:  "
142                     + clientAddress);
143         return null;
144     }
145 
146     /**
147      * Handle DHCPREQUEST 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 handleREQUEST(InetSocketAddress localAddress,
156             InetSocketAddress clientAddress, DhcpMessage request)
157             throws DhcpException {
158         if (logger.isDebugEnabled())
159             logger.debug("Got REQUEST message: " + request + " from:  "
160                     + clientAddress);
161         return null;
162     }
163 
164     /**
165      * Handle DHCPDISCOVER message. The default implementation just ignores it.
166      * 
167      * @param localAddress
168      * @param clientAddress
169      * @param request the request message
170      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
171      *         it.
172      * @throws DhcpException
173      */
174     protected DhcpMessage handleDISCOVER(InetSocketAddress localAddress,
175             InetSocketAddress clientAddress, DhcpMessage request)
176             throws DhcpException {
177         if (logger.isDebugEnabled())
178             logger.debug("Got DISCOVER message: " + request + " from:  "
179                     + clientAddress);
180         return null;
181     }
182 
183     /**
184      * Handle DHCPOFFER message. The default implementation just ignores it.
185      * 
186      * @param localAddress
187      * @param clientAddress
188      * @param request the request message
189      * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)
190      *         it.
191      * @throws DhcpException
192      */
193     protected DhcpMessage handleOFFER(InetSocketAddress localAddress,
194             InetSocketAddress clientAddress, DhcpMessage request)
195             throws DhcpException {
196         if (logger.isDebugEnabled())
197             logger
198                     .debug("Got OFFER message: " + request + " from:  " + clientAddress);
199         return null;
200     }
201 
202     /**
203      * Initialize a general DHCP reply message. Sets:
204      * <ul>
205      * <li>op=BOOTREPLY
206      * <li>htype, hlen, xid, flags, giaddr, chaddr like in request message
207      * <li>hops, secs to 0.
208      * <li>server hostname to the hostname appropriate for the interface the
209      * request was received on
210      * <li>the server identifier set to the address of the interface the request
211      * was received on
212      * </ul>
213      * 
214      * @param localAddress
215      * @param request
216      * @return DhcpMessage
217      */
218     protected final DhcpMessage initGeneralReply(InetSocketAddress localAddress,
219             DhcpMessage request) {
220         final DhcpMessage reply = new DhcpMessage();
221 
222         reply.setOp(DhcpMessage.OP_BOOTREPLY);
223 
224         reply.setHardwareAddress(request.getHardwareAddress());
225         reply.setTransactionId(request.getTransactionId());
226         reply.setFlags(request.getFlags());
227         reply.setRelayAgentAddress(request.getRelayAgentAddress());
228 
229         // set server hostname
230         reply.setServerHostname(localAddress.getHostName());
231 
232         // set server identifier based on the IF on which we received the packet
233         reply.getOptions().add(new ServerIdentifier(localAddress.getAddress()));
234 
235         return reply;
236     }
237 
238     /**
239      * Check if an address is the zero-address
240      * 
241      * @param addr
242      * @return boolean
243      */
244     private boolean isZeroAddress(byte[] addr) {
245         for (int i = 0; i < addr.length; i++)
246             if (addr[i] != 0)
247                 return false;
248         return true;
249     }
250 
251     /**
252      * Determine address on which to base selection. If the relay agent address is
253      * set, we use the relay agent's address, otherwise we use the address we
254      * received the request from.
255      * 
256      * @param clientAddress
257      * @param request
258      * @return InetAddress
259      */
260     protected final InetAddress determineSelectionBase(
261             InetSocketAddress clientAddress, DhcpMessage request) {
262         // FIXME: do we know
263         // a) the interface address over which we received a message (!)
264         // b) the client address (if specified)
265         // c) the relay agent address?
266 
267         // if the relay agent address is set, we use it as the selection base
268         if (!isZeroAddress(request.getRelayAgentAddress().getAddress()))
269             return request.getRelayAgentAddress();
270 
271         return clientAddress.getAddress();
272     }
273 
274     /**
275      * Strip options that the client doesn't want, if the ParameterRequestList
276      * option is present.
277      * 
278      * @param request
279      * @param options
280      */
281     protected final void stripUnwantedOptions(DhcpMessage request,
282             OptionsField options) {
283         final ParameterRequestList prl = (ParameterRequestList) request
284                 .getOptions().get(ParameterRequestList.class);
285         if (null != prl) {
286             final byte[] list = prl.getData();
287             for (final Iterator i = options.iterator(); i.hasNext();) {
288                 final DhcpOption o = (DhcpOption) i.next();
289                 for (int j = 0; j < list.length; j++)
290                     if (list[j] == o.getTag())
291                         continue;
292                 i.remove();
293             }
294         }
295     }
296 }