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  
24  import java.net.InetAddress;
25  import java.net.InetSocketAddress;
26  
27  import org.apache.directory.server.dhcp.messages.DhcpMessage;
28  import org.apache.directory.server.dhcp.messages.MessageType;
29  import org.apache.directory.server.dhcp.service.DhcpService;
30  import org.apache.mina.core.service.IoHandlerAdapter;
31  import org.apache.mina.core.session.IdleStatus;
32  import org.apache.mina.core.session.IoSession;
33  import org.apache.mina.filter.codec.ProtocolCodecFilter;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  
38  /**
39   * Implementation of a DHCP protocol handler which delegates the work of
40   * generating replys to a DhcpService implementation.
41   * 
42   * @see org.apache.directory.server.dhcp.service.DhcpService
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   */
45  public class DhcpProtocolHandler extends IoHandlerAdapter
46  {
47      private static final Logger LOG = LoggerFactory.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       */
69      public DhcpProtocolHandler( DhcpService service )
70      {
71          this.dhcpService = service;
72      }
73  
74  
75      @Override
76      public void sessionCreated( IoSession session ) throws Exception
77      {
78          LOG.debug( "{} CREATED", session.getLocalAddress() );
79          session.getFilterChain().addFirst( "codec",
80              new ProtocolCodecFilter( new DhcpProtocolCodecFactory() ) );
81      }
82  
83  
84      @Override
85      public void sessionOpened( IoSession session )
86      {
87          LOG.debug( "{} -> {} OPENED", session.getRemoteAddress(), session
88              .getLocalAddress() );
89      }
90  
91  
92      @Override
93      public void sessionClosed( IoSession session )
94      {
95          LOG.debug( "{} -> {} CLOSED", session.getRemoteAddress(), session
96              .getLocalAddress() );
97      }
98  
99  
100     @Override
101     public void sessionIdle( IoSession session, IdleStatus status )
102     {
103         // ignore
104     }
105 
106 
107     @Override
108     public void exceptionCaught( IoSession session, Throwable cause )
109     {
110         LOG.error( "EXCEPTION CAUGHT ", cause );
111         cause.printStackTrace( System.out );
112 
113         session.closeNow();
114     }
115 
116 
117     @Override
118     public void messageReceived( IoSession session, Object message )
119         throws Exception
120     {
121         if ( LOG.isDebugEnabled() )
122         {
123             LOG.debug( "{} -> {} RCVD: {} ", message, session.getRemoteAddress(),
124                 session.getLocalAddress() );
125         }
126 
127         final DhcpMessage./../../../org/apache/directory/server/dhcp/messages/DhcpMessage.html#DhcpMessage">DhcpMessage request = ( DhcpMessage ) message;
128 
129         final DhcpMessage reply = dhcpService.getReplyFor(
130             ( InetSocketAddress ) session.getServiceAddress(),
131             ( InetSocketAddress ) session.getRemoteAddress(), request );
132 
133         if ( null != reply )
134         {
135             final InetSocketAddress isa = determineMessageDestination( request, reply );
136             session.write( reply, isa );
137         }
138     }
139 
140 
141     /**
142      * Determine where to send the message: <br>
143      * If the 'giaddr' field in a DHCP message from a client is non-zero, the
144      * server sends any return messages to the 'DHCP server' port on the BOOTP
145      * relay agent whose address appears in 'giaddr'. If the 'giaddr' field is
146      * zero and the 'ciaddr' field is nonzero, then the server unicasts DHCPOFFER
147      * and DHCPACK messages to the address in 'ciaddr'. If 'giaddr' is zero and
148      * 'ciaddr' is zero, and the broadcast bit is set, then the server broadcasts
149      * DHCPOFFER and DHCPACK messages to 0xffffffff. If the broadcast bit is not
150      * set and 'giaddr' is zero and 'ciaddr' is zero, then the server unicasts
151      * DHCPOFFER and DHCPACK messages to the client's hardware address and
152      * 'yiaddr' address. In all cases, when 'giaddr' is zero, the server
153      * broadcasts any DHCPNAK messages to 0xffffffff.
154      * 
155      * @param request
156      * @param reply
157      * @return
158      */
159     //This will suppress PMD.AvoidUsingHardCodedIP warnings in this class
160     @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
161     private InetSocketAddress determineMessageDestination( DhcpMessage request,
162         DhcpMessage reply )
163     {
164 
165         final MessageType mt = reply.getMessageType();
166 
167         if ( !isNullAddress( request.getRelayAgentAddress() ) )
168         {
169             // send to agent, if received via agent.
170             return new InetSocketAddress( request.getRelayAgentAddress(), SERVER_PORT );
171         }
172         else if ( null != mt && mt == MessageType.DHCPNAK )
173         {
174             // force broadcast for DHCPNAKs
175             return new InetSocketAddress( "255.255.255.255", 68 );
176         }
177         else
178         {
179             // not a NAK...
180             if ( !isNullAddress( request.getCurrentClientAddress() ) )
181             {
182                 // have a current address? unicast to it.
183                 return new InetSocketAddress( request.getCurrentClientAddress(),
184                     CLIENT_PORT );
185             }
186             else
187             {
188                 return new InetSocketAddress( "255.255.255.255", 68 );
189             }
190         }
191     }
192 
193 
194     /**
195      * Determine, whether the given address ist actually the null address
196      * "0.0.0.0".
197      * 
198      * @param relayAgentAddress
199      * @return
200      */
201     private boolean isNullAddress( InetAddress addr )
202     {
203         final byte[] a = addr.getAddress();
204 
205         for ( int i = 0; i < a.length; i++ )
206         {
207             if ( a[i] != 0 )
208             {
209                 return false;
210             }
211         }
212 
213         return true;
214     }
215 
216 
217     @Override
218     public void messageSent( IoSession session, Object message )
219     {
220         if ( LOG.isDebugEnabled() )
221         {
222             LOG.debug( "{} -> {} SENT: ", message, session.getRemoteAddress(),
223                 session.getLocalAddress() );
224         }
225     }
226 
227     
228     @Override
229     public void inputClosed( IoSession session )
230     {
231     }
232 }