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