View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  
21  package org.apache.directory.server.dhcp.protocol;
22  
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  
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.service.DhcpService;
29  import org.apache.mina.common.BroadcastIoSession;
30  import org.apache.mina.common.IdleStatus;
31  import org.apache.mina.common.IoHandler;
32  import org.apache.mina.common.IoSession;
33  import org.apache.mina.filter.codec.ProtocolCodecFilter;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * Implementation of a DHCP protocol handler which delegates the work of
39   * generating replys to a DhcpService implementation.
40   * 
41   * @see org.apache.directory.server.dhcp.service.DhcpService
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   * @version $Rev: 664295 $, $Date: 2008-06-07 09:48:16 +0200 (Sa, 07 Jun 2008) $
44   */
45  public class DhcpProtocolHandler implements IoHandler {
46      private static final Logger logger = LoggerFactory
47              .getLogger(DhcpProtocolHandler.class);
48  
49      /**
50       * Default DHCP client port
51       */
52      public static final int CLIENT_PORT = 68;
53  
54      /**
55       * Default DHCP server port
56       */
57      public static final int SERVER_PORT = 67;
58  
59      /**
60       * The DHCP service implementation. The implementation is supposed to be
61       * thread-safe.
62       */
63      private final DhcpService dhcpService;
64  
65      /**
66       * 
67       */
68      public DhcpProtocolHandler(DhcpService service) {
69          this.dhcpService = service;
70      }
71  
72      public void sessionCreated(IoSession session) throws Exception {
73          logger.debug("{} CREATED", session.getLocalAddress());
74          session.getFilterChain().addFirst("codec",
75                  new ProtocolCodecFilter(new DhcpProtocolCodecFactory()));
76      }
77  
78      public void sessionOpened(IoSession session) {
79          logger.debug("{} -> {} OPENED", session.getRemoteAddress(), session
80                  .getLocalAddress());
81      }
82  
83      public void sessionClosed(IoSession session) {
84          logger.debug("{} -> {} CLOSED", session.getRemoteAddress(), session
85                  .getLocalAddress());
86      }
87  
88      public void sessionIdle(IoSession session, IdleStatus status) {
89          // ignore
90      }
91  
92      public void exceptionCaught(IoSession session, Throwable cause) {
93          logger.error("EXCEPTION CAUGHT ", cause);
94          cause.printStackTrace(System.out);
95  
96          session.close();
97      }
98  
99      public void messageReceived(IoSession session, Object message)
100             throws Exception {
101         if (logger.isDebugEnabled())
102             logger.debug("{} -> {} RCVD: {} " + message, session.getRemoteAddress(),
103                     session.getLocalAddress());
104 
105         final DhcpMessage request = (DhcpMessage) message;
106 
107         final DhcpMessage reply = dhcpService.getReplyFor(
108                 (InetSocketAddress) session.getServiceAddress(),
109                 (InetSocketAddress) session.getRemoteAddress(), request);
110 
111         if (null != reply) {
112             final InetSocketAddress isa = determineMessageDestination(request, reply);
113             ((BroadcastIoSession) session).write(reply, isa);
114         }
115     }
116 
117     /**
118      * Determine where to send the message: <br>
119      * If the 'giaddr' field in a DHCP message from a client is non-zero, the
120      * server sends any return messages to the 'DHCP server' port on the BOOTP
121      * relay agent whose address appears in 'giaddr'. If the 'giaddr' field is
122      * zero and the 'ciaddr' field is nonzero, then the server unicasts DHCPOFFER
123      * and DHCPACK messages to the address in 'ciaddr'. If 'giaddr' is zero and
124      * 'ciaddr' is zero, and the broadcast bit is set, then the server broadcasts
125      * DHCPOFFER and DHCPACK messages to 0xffffffff. If the broadcast bit is not
126      * set and 'giaddr' is zero and 'ciaddr' is zero, then the server unicasts
127      * DHCPOFFER and DHCPACK messages to the client's hardware address and
128      * 'yiaddr' address. In all cases, when 'giaddr' is zero, the server
129      * broadcasts any DHCPNAK messages to 0xffffffff.
130      * 
131      * @param request
132      * @param reply
133      * @return
134      */
135     private InetSocketAddress determineMessageDestination(DhcpMessage request,
136             DhcpMessage reply) {
137 
138         final MessageType mt = reply.getMessageType();
139         if (!isNullAddress(request.getRelayAgentAddress()))
140             // send to agent, if received via agent.
141             return new InetSocketAddress(request.getRelayAgentAddress(), SERVER_PORT);
142         else if (null != mt && mt == MessageType.DHCPNAK)
143             // force broadcast for DHCPNAKs
144             return new InetSocketAddress("255.255.255.255", 68);
145         else // not a NAK...
146         if (!isNullAddress(request.getCurrentClientAddress()))
147             // have a current address? unicast to it.
148             return new InetSocketAddress(request.getCurrentClientAddress(),
149                     CLIENT_PORT);
150         else
151             return new InetSocketAddress("255.255.255.255", 68);
152     }
153 
154     /**
155      * Determine, whether the given address ist actually the null address
156      * "0.0.0.0".
157      * 
158      * @param relayAgentAddress
159      * @return
160      */
161     private boolean isNullAddress(InetAddress addr) {
162         final byte a[] = addr.getAddress();
163         for (int i = 0; i < a.length; i++)
164             if (a[i] != 0)
165                 return false;
166         return true;
167     }
168 
169     public void messageSent(IoSession session, Object message) {
170         if (logger.isDebugEnabled())
171             logger.debug("{} -> {} SENT: " + message, session.getRemoteAddress(),
172                     session.getLocalAddress());
173     }
174 }