View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   * 
19   */
20  
21  package org.apache.directory.server.dhcp.io;
22  
23  
24  import java.io.UnsupportedEncodingException;
25  import java.net.InetAddress;
26  import java.net.UnknownHostException;
27  import java.nio.ByteBuffer;
28  import java.util.Arrays;
29  
30  import org.apache.directory.server.dhcp.DhcpException;
31  import org.apache.directory.server.dhcp.messages.DhcpMessage;
32  import org.apache.directory.server.dhcp.messages.HardwareAddress;
33  import org.apache.directory.server.dhcp.options.DhcpOption;
34  import org.apache.directory.server.dhcp.options.OptionsField;
35  import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
36  import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption;
37  import org.apache.directory.server.i18n.I18n;
38  
39  
40  /**
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42   */
43  public class DhcpMessageDecoder
44  {
45  
46      /**
47       * Convert a byte buffer into a DhcpMessage.
48       * 
49       * @return a DhcpMessage.
50       * @param buffer ByteBuffer to convert to a DhcpMessage object
51       * @throws DhcpException 
52       */
53      public DhcpMessage decode( ByteBuffer buffer ) throws DhcpException
54      {
55          byte op = buffer.get();
56  
57          short htype = ( short ) ( buffer.get() & 0xff );
58          short hlen = ( short ) ( buffer.get() & 0xff );
59          short hops = ( short ) ( buffer.get() & 0xff );
60          int xid = buffer.getInt();
61          int secs = buffer.getShort() & 0xffff;
62          short flags = buffer.getShort();
63  
64          InetAddress ciaddr = decodeAddress( buffer );
65          InetAddress yiaddr = decodeAddress( buffer );
66          InetAddress siaddr = decodeAddress( buffer );
67          InetAddress giaddr = decodeAddress( buffer );
68  
69          byte[] chaddr = decodeBytes( buffer, 16 );
70  
71          String sname = decodeString( buffer, 64 );
72          String file = decodeString( buffer, 128 );
73  
74          OptionsField options = decodeOptions( buffer );
75  
76          // message type option: may be null if option isn't set (BOOTP)
77          DhcpMessageType mto = ( DhcpMessageType ) options.get( DhcpMessageType.class );
78  
79          return new DhcpMessage( null != mto ? mto.getType() : null, op, new HardwareAddress( htype, hlen, chaddr ),
80              hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, sname, file, options );
81      }
82  
83  
84      /**
85       * @param buffer
86       * @param len
87       * @return
88       */
89      private static byte[] decodeBytes( ByteBuffer buffer, int len )
90      {
91          byte[] bytes = new byte[len];
92          buffer.get( bytes );
93          return bytes;
94      }
95  
96  
97      /**
98       * @param buffer
99       * @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         while ( bytes[slen] != 0 )
109             slen++;
110 
111         try
112         {
113             return new String( bytes, 0, slen, "ASCII" );
114         }
115         catch ( UnsupportedEncodingException e )
116         {
117             throw new RuntimeException( I18n.err( I18n.ERR_635 ), e );
118         }
119     }
120 
121 
122     /**
123      * Read a 4-byte inet address from the buffer.
124      * 
125      * @param buffer
126      * @return
127      * @throws UnknownHostException
128      */
129     private static InetAddress decodeAddress( ByteBuffer buffer )
130     {
131         byte[] addr = new byte[4];
132         buffer.get( addr );
133         try
134         {
135             return InetAddress.getByAddress( addr );
136         }
137         catch ( UnknownHostException e )
138         {
139             // should not happen
140             return null;
141         }
142     }
143 
144     private static final byte[] VENDOR_MAGIC_COOKIE =
145         { ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 };
146 
147 
148     public OptionsField decodeOptions( ByteBuffer message ) throws DhcpException
149     {
150         byte[] magicCookie = new byte[4];
151         message.get( magicCookie );
152 
153         if ( !Arrays.equals( VENDOR_MAGIC_COOKIE, magicCookie ) )
154         {
155             throw new DhcpException( "Parse exception." );
156         }
157 
158         byte code;
159         byte length;
160         byte value[];
161 
162         OptionsField options = new OptionsField();
163 
164         while ( true )
165         {
166             code = message.get();
167             if ( code == 0 ) // pad option
168                 continue;
169 
170             if ( code == -1 ) // end option
171                 break;
172 
173             length = message.get();
174             value = new byte[length];
175             message.get( value );
176 
177             options.add( getOptionInstance( code, value ) );
178         }
179 
180         return options;
181     }
182 
183 
184     private DhcpOption getOptionInstance( int tag, byte[] value ) throws DhcpException
185     {
186         try
187         {
188             Class c = DhcpOption.getClassByTag( tag );
189 
190             DhcpOption o = null != c ? ( DhcpOption ) c.newInstance() : new UnrecognizedOption( ( byte ) tag );
191             o.setData( value );
192 
193             return o;
194         }
195         catch ( Exception e )
196         {
197             throw new DhcpException( I18n.err( I18n.ERR_636, e.toString() ) );
198         }
199     }
200 }