blob: 480e51e5393b23dbe193a0aef0ff5c254b38d0e1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.dhcp.io;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.apache.directory.server.dhcp.DhcpException;
import org.apache.directory.server.dhcp.messages.DhcpMessage;
import org.apache.directory.server.dhcp.messages.HardwareAddress;
import org.apache.directory.server.dhcp.options.DhcpOption;
import org.apache.directory.server.dhcp.options.OptionsField;
import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption;
import org.apache.directory.server.i18n.I18n;
/**
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class DhcpMessageDecoder
{
/**
* Convert a byte buffer into a DhcpMessage.
*
* @return a DhcpMessage.
* @param buffer ByteBuffer to convert to a DhcpMessage object
* @throws DhcpException
*/
public DhcpMessage decode( ByteBuffer buffer ) throws DhcpException
{
byte op = buffer.get();
short htype = ( short ) ( buffer.get() & 0xff );
short hlen = ( short ) ( buffer.get() & 0xff );
short hops = ( short ) ( buffer.get() & 0xff );
int xid = buffer.getInt();
int secs = buffer.getShort() & 0xffff;
short flags = buffer.getShort();
InetAddress ciaddr = decodeAddress( buffer );
InetAddress yiaddr = decodeAddress( buffer );
InetAddress siaddr = decodeAddress( buffer );
InetAddress giaddr = decodeAddress( buffer );
byte[] chaddr = decodeBytes( buffer, 16 );
String sname = decodeString( buffer, 64 );
String file = decodeString( buffer, 128 );
OptionsField options = decodeOptions( buffer );
// message type option: may be null if option isn't set (BOOTP)
DhcpMessageType mto = ( DhcpMessageType ) options.get( DhcpMessageType.class );
return new DhcpMessage( null != mto ? mto.getType() : null, op, new HardwareAddress( htype, hlen, chaddr ),
hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, sname, file, options );
}
/**
* @param buffer
* @param len
* @return
*/
private static byte[] decodeBytes( ByteBuffer buffer, int len )
{
byte[] bytes = new byte[len];
buffer.get( bytes );
return bytes;
}
/**
* @param buffer
* @return
*/
private static String decodeString( ByteBuffer buffer, int len )
{
byte[] bytes = new byte[len];
buffer.get( bytes );
// find zero-terminator
int slen = 0;
while ( bytes[slen] != 0 )
{
slen++;
}
try
{
return new String( bytes, 0, slen, "ASCII" );
}
catch ( UnsupportedEncodingException e )
{
throw new RuntimeException( I18n.err( I18n.ERR_635 ), e );
}
}
/**
* Read a 4-byte inet address from the buffer.
*
* @param buffer
* @return
* @throws UnknownHostException
*/
private static InetAddress decodeAddress( ByteBuffer buffer )
{
byte[] addr = new byte[4];
buffer.get( addr );
try
{
return InetAddress.getByAddress( addr );
}
catch ( UnknownHostException e )
{
// should not happen
return null;
}
}
private static final byte[] VENDOR_MAGIC_COOKIE =
{ ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 };
public OptionsField decodeOptions( ByteBuffer message ) throws DhcpException
{
byte[] magicCookie = new byte[4];
message.get( magicCookie );
if ( !Arrays.equals( VENDOR_MAGIC_COOKIE, magicCookie ) )
{
throw new DhcpException( "Parse exception." );
}
byte code;
byte length;
byte value[];
OptionsField options = new OptionsField();
while ( true )
{
code = message.get();
if ( code == 0 ) // pad option
{
continue;
}
if ( code == -1 ) // end option
{
break;
}
length = message.get();
value = new byte[length];
message.get( value );
options.add( getOptionInstance( code, value ) );
}
return options;
}
private DhcpOption getOptionInstance( int tag, byte[] value ) throws DhcpException
{
try
{
Class c = DhcpOption.getClassByTag( tag );
DhcpOption o = null != c ? ( DhcpOption ) c.newInstance() : new UnrecognizedOption( ( byte ) tag );
o.setData( value );
return o;
}
catch ( Exception e )
{
throw new DhcpException( I18n.err( I18n.ERR_636, e.toString() ) );
}
}
}