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 021 package org.apache.directory.server.dhcp.io; 022 023 024 import java.io.UnsupportedEncodingException; 025 import java.net.InetAddress; 026 import java.net.UnknownHostException; 027 import java.nio.ByteBuffer; 028 import java.util.Arrays; 029 030 import org.apache.directory.server.dhcp.DhcpException; 031 import org.apache.directory.server.dhcp.messages.DhcpMessage; 032 import org.apache.directory.server.dhcp.messages.HardwareAddress; 033 import org.apache.directory.server.dhcp.options.DhcpOption; 034 import org.apache.directory.server.dhcp.options.OptionsField; 035 import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType; 036 import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption; 037 import org.apache.directory.server.i18n.I18n; 038 039 040 /** 041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 042 * @version $Rev: 902331 $, $Date: 2010-01-23 02:41:35 +0100 (Sat, 23 Jan 2010) $ 043 */ 044 public class DhcpMessageDecoder 045 { 046 047 /** 048 * Convert a byte buffer into a DhcpMessage. 049 * 050 * @return a DhcpMessage. 051 * @param buffer ByteBuffer to convert to a DhcpMessage object 052 * @throws DhcpException 053 */ 054 public DhcpMessage decode( ByteBuffer buffer ) throws DhcpException 055 { 056 byte op = buffer.get(); 057 058 short htype = ( short ) ( buffer.get() & 0xff ); 059 short hlen = ( short ) ( buffer.get() & 0xff ); 060 short hops = ( short ) ( buffer.get() & 0xff ); 061 int xid = buffer.getInt(); 062 int secs = buffer.getShort() & 0xffff; 063 short flags = buffer.getShort(); 064 065 InetAddress ciaddr = decodeAddress( buffer ); 066 InetAddress yiaddr = decodeAddress( buffer ); 067 InetAddress siaddr = decodeAddress( buffer ); 068 InetAddress giaddr = decodeAddress( buffer ); 069 070 byte[] chaddr = decodeBytes( buffer, 16 ); 071 072 String sname = decodeString( buffer, 64 ); 073 String file = decodeString( buffer, 128 ); 074 075 OptionsField options = decodeOptions( buffer ); 076 077 // message type option: may be null if option isn't set (BOOTP) 078 DhcpMessageType mto = ( DhcpMessageType ) options.get( DhcpMessageType.class ); 079 080 return new DhcpMessage( null != mto ? mto.getType() : null, op, new HardwareAddress( htype, hlen, chaddr ), 081 hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, sname, file, options ); 082 } 083 084 085 /** 086 * @param buffer 087 * @param len 088 * @return 089 */ 090 private static byte[] decodeBytes( ByteBuffer buffer, int len ) 091 { 092 byte[] bytes = new byte[len]; 093 buffer.get( bytes ); 094 return bytes; 095 } 096 097 098 /** 099 * @param buffer 100 * @return 101 */ 102 private static String decodeString( ByteBuffer buffer, int len ) 103 { 104 byte[] bytes = new byte[len]; 105 buffer.get( bytes ); 106 107 // find zero-terminator 108 int slen = 0; 109 while ( bytes[slen] != 0 ) 110 slen++; 111 112 try 113 { 114 return new String( bytes, 0, slen, "ASCII" ); 115 } 116 catch ( UnsupportedEncodingException e ) 117 { 118 throw new RuntimeException( I18n.err( I18n.ERR_635 ), e ); 119 } 120 } 121 122 123 /** 124 * Read a 4-byte inet address from the buffer. 125 * 126 * @param buffer 127 * @return 128 * @throws UnknownHostException 129 */ 130 private static InetAddress decodeAddress( ByteBuffer buffer ) 131 { 132 byte[] addr = new byte[4]; 133 buffer.get( addr ); 134 try 135 { 136 return InetAddress.getByAddress( addr ); 137 } 138 catch ( UnknownHostException e ) 139 { 140 // should not happen 141 return null; 142 } 143 } 144 145 private static final byte[] VENDOR_MAGIC_COOKIE = 146 { ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 }; 147 148 149 public OptionsField decodeOptions( ByteBuffer message ) throws DhcpException 150 { 151 byte[] magicCookie = new byte[4]; 152 message.get( magicCookie ); 153 154 if ( !Arrays.equals( VENDOR_MAGIC_COOKIE, magicCookie ) ) 155 { 156 throw new DhcpException( "Parse exception." ); 157 } 158 159 byte code; 160 byte length; 161 byte value[]; 162 163 OptionsField options = new OptionsField(); 164 165 while ( true ) 166 { 167 code = message.get(); 168 if ( code == 0 ) // pad option 169 continue; 170 171 if ( code == -1 ) // end option 172 break; 173 174 length = message.get(); 175 value = new byte[length]; 176 message.get( value ); 177 178 options.add( getOptionInstance( code, value ) ); 179 } 180 181 return options; 182 } 183 184 185 private DhcpOption getOptionInstance( int tag, byte[] value ) throws DhcpException 186 { 187 try 188 { 189 Class c = DhcpOption.getClassByTag( tag ); 190 191 DhcpOption o = null != c ? ( DhcpOption ) c.newInstance() : new UnrecognizedOption( ( byte ) tag ); 192 o.setData( value ); 193 194 return o; 195 } 196 catch ( Exception e ) 197 { 198 throw new DhcpException( I18n.err( I18n.ERR_636, e.toString() ) ); 199 } 200 } 201 }