001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020
021 package org.apache.directory.server.dhcp.protocol;
022
023 import java.net.InetAddress;
024 import java.net.InetSocketAddress;
025
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.service.DhcpService;
029 import org.apache.mina.core.service.IoHandler;
030 import org.apache.mina.core.session.IdleStatus;
031 import org.apache.mina.core.session.IoSession;
032 import org.apache.mina.filter.codec.ProtocolCodecFilter;
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035
036 /**
037 * Implementation of a DHCP protocol handler which delegates the work of
038 * generating replys to a DhcpService implementation.
039 *
040 * @see org.apache.directory.server.dhcp.service.DhcpService
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 * @version $Rev: 725712 $, $Date: 2008-12-11 17:32:04 +0200 (Thu, 11 Dec 2008) $
043 */
044 public class DhcpProtocolHandler implements IoHandler {
045 private static final Logger logger = LoggerFactory
046 .getLogger(DhcpProtocolHandler.class);
047
048 /**
049 * Default DHCP client port
050 */
051 public static final int CLIENT_PORT = 68;
052
053 /**
054 * Default DHCP server port
055 */
056 public static final int SERVER_PORT = 67;
057
058 /**
059 * The DHCP service implementation. The implementation is supposed to be
060 * thread-safe.
061 */
062 private final DhcpService dhcpService;
063
064 /**
065 *
066 */
067 public DhcpProtocolHandler(DhcpService service) {
068 this.dhcpService = service;
069 }
070
071 public void sessionCreated(IoSession session) throws Exception {
072 logger.debug("{} CREATED", session.getLocalAddress());
073 session.getFilterChain().addFirst("codec",
074 new ProtocolCodecFilter(new DhcpProtocolCodecFactory()));
075 }
076
077 public void sessionOpened(IoSession session) {
078 logger.debug("{} -> {} OPENED", session.getRemoteAddress(), session
079 .getLocalAddress());
080 }
081
082 public void sessionClosed(IoSession session) {
083 logger.debug("{} -> {} CLOSED", session.getRemoteAddress(), session
084 .getLocalAddress());
085 }
086
087 public void sessionIdle(IoSession session, IdleStatus status) {
088 // ignore
089 }
090
091 public void exceptionCaught(IoSession session, Throwable cause) {
092 logger.error("EXCEPTION CAUGHT ", cause);
093 cause.printStackTrace(System.out);
094
095 session.close( true );
096 }
097
098 public void messageReceived(IoSession session, Object message)
099 throws Exception {
100 if (logger.isDebugEnabled())
101 logger.debug("{} -> {} RCVD: {} " + message, session.getRemoteAddress(),
102 session.getLocalAddress());
103
104 final DhcpMessage request = (DhcpMessage) message;
105
106 final DhcpMessage reply = dhcpService.getReplyFor(
107 (InetSocketAddress) session.getServiceAddress(),
108 (InetSocketAddress) session.getRemoteAddress(), request);
109
110 if (null != reply) {
111 final InetSocketAddress isa = determineMessageDestination(request, reply);
112 session.write(reply, isa);
113 }
114 }
115
116 /**
117 * Determine where to send the message: <br>
118 * If the 'giaddr' field in a DHCP message from a client is non-zero, the
119 * server sends any return messages to the 'DHCP server' port on the BOOTP
120 * relay agent whose address appears in 'giaddr'. If the 'giaddr' field is
121 * zero and the 'ciaddr' field is nonzero, then the server unicasts DHCPOFFER
122 * and DHCPACK messages to the address in 'ciaddr'. If 'giaddr' is zero and
123 * 'ciaddr' is zero, and the broadcast bit is set, then the server broadcasts
124 * DHCPOFFER and DHCPACK messages to 0xffffffff. If the broadcast bit is not
125 * set and 'giaddr' is zero and 'ciaddr' is zero, then the server unicasts
126 * DHCPOFFER and DHCPACK messages to the client's hardware address and
127 * 'yiaddr' address. In all cases, when 'giaddr' is zero, the server
128 * broadcasts any DHCPNAK messages to 0xffffffff.
129 *
130 * @param request
131 * @param reply
132 * @return
133 */
134 private InetSocketAddress determineMessageDestination(DhcpMessage request,
135 DhcpMessage reply) {
136
137 final MessageType mt = reply.getMessageType();
138 if (!isNullAddress(request.getRelayAgentAddress()))
139 // send to agent, if received via agent.
140 return new InetSocketAddress(request.getRelayAgentAddress(), SERVER_PORT);
141 else if (null != mt && mt == MessageType.DHCPNAK)
142 // force broadcast for DHCPNAKs
143 return new InetSocketAddress("255.255.255.255", 68);
144 else // not a NAK...
145 if (!isNullAddress(request.getCurrentClientAddress()))
146 // have a current address? unicast to it.
147 return new InetSocketAddress(request.getCurrentClientAddress(),
148 CLIENT_PORT);
149 else
150 return new InetSocketAddress("255.255.255.255", 68);
151 }
152
153 /**
154 * Determine, whether the given address ist actually the null address
155 * "0.0.0.0".
156 *
157 * @param relayAgentAddress
158 * @return
159 */
160 private boolean isNullAddress(InetAddress addr) {
161 final byte a[] = addr.getAddress();
162 for (int i = 0; i < a.length; i++)
163 if (a[i] != 0)
164 return false;
165 return true;
166 }
167
168 public void messageSent(IoSession session, Object message) {
169 if (logger.isDebugEnabled())
170 logger.debug("{} -> {} SENT: " + message, session.getRemoteAddress(),
171 session.getLocalAddress());
172 }
173 }