| /* |
| * 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() ) ); |
| } |
| } |
| } |