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 021package org.apache.directory.server.dhcp.protocol; 022 023 024import java.net.InetAddress; 025import java.net.InetSocketAddress; 026 027import org.apache.directory.server.dhcp.messages.DhcpMessage; 028import org.apache.directory.server.dhcp.messages.MessageType; 029import org.apache.directory.server.dhcp.service.DhcpService; 030import org.apache.mina.core.service.IoHandlerAdapter; 031import org.apache.mina.core.session.IdleStatus; 032import org.apache.mina.core.session.IoSession; 033import org.apache.mina.filter.codec.ProtocolCodecFilter; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037 038/** 039 * Implementation of a DHCP protocol handler which delegates the work of 040 * generating replys to a DhcpService implementation. 041 * 042 * @see org.apache.directory.server.dhcp.service.DhcpService 043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 044 */ 045public class DhcpProtocolHandler extends IoHandlerAdapter 046{ 047 private static final Logger LOG = LoggerFactory.getLogger( DhcpProtocolHandler.class ); 048 049 /** 050 * Default DHCP client port 051 */ 052 public static final int CLIENT_PORT = 68; 053 054 /** 055 * Default DHCP server port 056 */ 057 public static final int SERVER_PORT = 67; 058 059 /** 060 * The DHCP service implementation. The implementation is supposed to be 061 * thread-safe. 062 */ 063 private final DhcpService dhcpService; 064 065 066 /** 067 * 068 */ 069 public DhcpProtocolHandler( DhcpService service ) 070 { 071 this.dhcpService = service; 072 } 073 074 075 @Override 076 public void sessionCreated( IoSession session ) throws Exception 077 { 078 LOG.debug( "{} CREATED", session.getLocalAddress() ); 079 session.getFilterChain().addFirst( "codec", 080 new ProtocolCodecFilter( new DhcpProtocolCodecFactory() ) ); 081 } 082 083 084 @Override 085 public void sessionOpened( IoSession session ) 086 { 087 LOG.debug( "{} -> {} OPENED", session.getRemoteAddress(), session 088 .getLocalAddress() ); 089 } 090 091 092 @Override 093 public void sessionClosed( IoSession session ) 094 { 095 LOG.debug( "{} -> {} CLOSED", session.getRemoteAddress(), session 096 .getLocalAddress() ); 097 } 098 099 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 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}