001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with this
004 * work for additional information regarding copyright ownership. The ASF
005 * licenses this file to you under the Apache License, Version 2.0 (the
006 * "License"); you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 * License for the specific language governing permissions and limitations under
015 * the License.
016 *
017 */
018
019 package org.apache.directory.server.dhcp.service;
020
021 import java.net.InetAddress;
022 import java.net.InetSocketAddress;
023 import java.util.Iterator;
024
025 import org.apache.directory.server.dhcp.DhcpException;
026 import org.apache.directory.server.dhcp.messages.DhcpMessage;
027 import org.apache.directory.server.dhcp.messages.MessageType;
028 import org.apache.directory.server.dhcp.options.DhcpOption;
029 import org.apache.directory.server.dhcp.options.OptionsField;
030 import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
031 import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
032 import org.slf4j.Logger;
033 import org.slf4j.LoggerFactory;
034
035 /**
036 * Abstract implementation of the server-side DHCP protocol. This class just
037 * provides some utility methods and dispatches server-bound messages to handler
038 * methods which can be overridden to provide the functionality.
039 * <p>
040 * Client-bound messages and BOOTP messages are ignored.
041 *
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $
044 *
045 */
046 public abstract class AbstractDhcpService implements DhcpService {
047 private static final Logger logger = LoggerFactory
048 .getLogger(AbstractDhcpService.class);
049
050 /*
051 * @see org.apache.directory.server.dhcp.DhcpService#getReplyFor(org.apache.directory.server.dhcp.messages.DhcpMessage)
052 */
053 public final DhcpMessage getReplyFor(InetSocketAddress localAddress,
054 InetSocketAddress clientAddress, DhcpMessage request)
055 throws DhcpException {
056 // ignore messages with an op != REQUEST/REPLY
057 if (request.getOp() != DhcpMessage.OP_BOOTREQUEST
058 && request.getOp() != DhcpMessage.OP_BOOTREPLY)
059 return null;
060
061 // message type option MUST be set - we don't support plain BOOTP.
062 if (null == request.getMessageType()) {
063 logger.warn("Missing message type option - plain BOOTP not supported.");
064 return null;
065 }
066
067 // dispatch based on the message type
068 switch (request.getMessageType().getCode()){
069 // client-to-server messages
070 case MessageType.CODE_DHCPDISCOVER :
071 return handleDISCOVER(localAddress, clientAddress, request);
072 case MessageType.CODE_DHCPREQUEST :
073 return handleREQUEST(localAddress, clientAddress, request);
074 case MessageType.CODE_DHCPRELEASE :
075 return handleRELEASE(localAddress, clientAddress, request);
076 case MessageType.CODE_DHCPINFORM :
077 return handleINFORM(localAddress, clientAddress, request);
078
079 case MessageType.CODE_DHCPOFFER :
080 return handleOFFER(localAddress, clientAddress, request);
081
082 // server-to-client messages
083 case MessageType.CODE_DHCPDECLINE :
084 case MessageType.CODE_DHCPACK :
085 case MessageType.CODE_DHCPNAK :
086 return null; // just ignore them
087
088 default :
089 return handleUnknownMessage(clientAddress, request);
090 }
091 }
092
093 /**
094 * Handle unknown DHCP message. The default implementation just logs and
095 * ignores it.
096 *
097 * @param clientAddress
098 * @param request the request message
099 * @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 }