View Javadoc

1   /*
2    *   Copyright 2004-2005 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.asn1.ber ;
18  
19  
20  import java.nio.ByteBuffer;
21  import java.util.Stack;
22  
23  import org.apache.asn1.codec.DecoderException;
24  import org.apache.asn1.codec.stateful.DecoderCallback;
25  import org.apache.asn1.codec.stateful.DecoderMonitor;
26  import org.apache.asn1.codec.stateful.DecoderMonitorAdapter;
27  import org.apache.asn1.codec.stateful.StatefulDecoder;
28  import org.apache.commons.lang.ArrayUtils;
29  
30  
31  /***
32   * A decoder that decodes BER encoded bytes to Tag Value Length (TLV) tuples.
33   * This decoder is a low level event based parser which operates in a fashion
34   * similar to the way SAX works except the elements of concern are the tag,
35   * length, and value entities.  The decoder is a state machine which processes
36   * input as it is made available.
37   * <p>
38   * A Stack is used to track the state of the decoder between decode calls.  It
39   * maintains the nesting of TLV tuples.  Rather than creating new TLV tuple
40   * instances every time a single tuple is reused for primitive types and new
41   * tlv tuples are cloned for constructed types which are pushed onto the stack.
42   * The tuple fed to the callback must therefore be used very carefully - its
43   * values must be copied to prevent their loss if they are to be used later
44   * after the callback invokation has returned.
45   * </p>
46   * <p>
47   * Note that all tuples are not created equal.  Constructed TLVs nesting others
48   * will have null value members or empty buffers.  Only TLV tuples of primitive
49   * types or the leaf TLV tuples of the TLV tuple tree will contain non null
50   * values.  Therefore the nature of a TLV tuple should be investigated by
51   * callbacks before attempting to interpret their values.  Also this decoder
52   * chunks value data returning it in parts rather than in one complete peice
53   * in the end.  The value of the TLV Tuple returned is the part of the value
54   * that was read from the input fed into the decoder.  These 'chunks' returned
55   * by callback makes it so there are no size limits to the value of a TLV. Again
56   * to reiterate chunking on values is only performed on primitive TLV Tuple
57   * types.
58   * </p>
59   *
60   * @author <a href="mailto:dev@directory.apache.org">
61   * Apache Directory Project</a>
62   * @version $Rev: 157644 $
63   */
64  public class BERDecoder implements StatefulDecoder, DecoderCallback
65  {
66      /*** empty byte buffer to be reused */
67      private static final ByteBuffer EMPTY_BUFFER =
68          ByteBuffer.wrap( ArrayUtils.EMPTY_BYTE_ARRAY ) ;
69      /*** the callback used by this decoder */
70      private static final BERDecoderCallback DEFAULT_CALLBACK =
71          new BERDecoderCallbackAdapter() ;
72      /*** the monitor used by this decoder */
73      private static final DecoderMonitor DEFAULT_MONITOR =
74          new DecoderMonitorAdapter() ;
75  
76      /*** this decoder's callback */
77      private BERDecoderCallback cb = DEFAULT_CALLBACK ;
78      /*** the monitor used by this decoder */
79      private DecoderMonitor monitor = DEFAULT_MONITOR ;
80  
81      /*** the single TLV tuple used by this decoder */
82      private final Tuple tlv = new Tuple() ;
83  
84      /*** a decoder used to decode tag octets */
85      private final TagDecoder tagDecoder = new TagDecoder() ;
86      /*** a decoder used to decode length octets */
87      private final LengthDecoder lengthDecoder = new LengthDecoder() ;
88  
89      /*** stack of nested/constructed TLV tuples */
90      private final Stack tlvStack = new Stack() ;
91  
92      /*** the state of this decoder */
93      private BERDecoderState state = BERDecoderState.getStartState() ;
94  
95  
96      /***
97       * Creates a stateful BER decoder which limits the tuple's value size.
98       */
99      public BERDecoder()
100     {
101         tagDecoder.setCallback( this ) ;
102         lengthDecoder.setCallback( this ) ;
103     }
104 
105 
106     // ------------------------------------------------------------------------
107     // StatefulDecoder Methods
108     // ------------------------------------------------------------------------
109 
110 
111     /***
112      * Expects a ByteBuffer containing BER encoded data.
113      *
114      * @see org.apache.asn1.codec.stateful.StatefulDecoder#decode(
115      * java.lang.Object)
116      * @throws ClassCastException if the encoded argument is not a ByteBuffer
117      * @throws IllegalArgumentException if the buffer is null or empty
118      */
119     public void decode( Object encoded ) throws DecoderException
120     {
121         ByteBuffer buf = ( ByteBuffer ) encoded ;
122 
123         /* --------------------------------------------------------------------
124            Handle any unusual input by informing the monitor.
125            ------------------------------------------------------------------ */
126 
127         if ( buf == null && monitor != null )
128         {
129             String msg = "ignoring null argument to decode()" ;
130             monitor.warning( this, new IllegalArgumentException( msg ) ) ;
131             return ;
132         }
133 
134         if ( buf.remaining() == 0 && monitor != null )
135         {
136             String msg = "ignoring empty buffer" ;
137             monitor.warning( this, new IllegalArgumentException( msg ) ) ;
138             return ;
139         }
140 
141         /*
142          * This loop is used instead of costly recursion.  This requires each
143          * of the statewise decode methods to process bytes from the buffer.  If
144          * they can process enough to switch state they do and return
145          * immediately.  This loop makes sure the next processing state is
146          * handled if there is more data for that state.
147          */
148         while ( buf.hasRemaining() )
149         {
150             switch( state.getValue() )
151             {
152                 case( BERDecoderState.TAG_VAL ):
153                     tagDecoder.decode( buf ) ;
154                     break ;
155                 case( BERDecoderState.LENGTH_VAL ):
156                     lengthDecoder.decode( buf ) ;
157                     break ;
158                 case( BERDecoderState.VALUE_VAL ):
159                     decodeValue( buf ) ;
160                     break ;
161             }
162         }
163     }
164 
165 
166     /* (non-Javadoc)
167      * @see org.apache.asn1.codec.stateful.StatefulDecoder#setCallback(
168      * org.apache.asn1.codec.stateful.DecoderCallback)
169      */
170     public void setCallback( DecoderCallback cb )
171     {
172         this.cb = ( BERDecoderCallback ) cb ;
173     }
174 
175 
176     /* (non-Javadoc)
177      * @see org.apache.asn1.codec.stateful.StatefulDecoder#setDecoderMonitor(
178      * org.apache.asn1.codec.stateful.DecoderMonitor)
179      */
180     public void setDecoderMonitor( DecoderMonitor monitor )
181     {
182         this.monitor = monitor ;
183     }
184 
185 
186     // ------------------------------------------------------------------------
187     // State Based Decode Methods
188     // ------------------------------------------------------------------------
189 
190 
191     /***
192      * Extracts the value portion from the buffer for a primitive type.
193      *
194      * @param buf the byte byffer containing BER encoded data
195      */
196     private void decodeValue( ByteBuffer buf )
197     {
198         int needToRead = Length.UNDEFINED ;
199 
200         /*
201          * setup to start decoding the value by figuring how much we need to
202          * read at this point - previous reads of the value may have already
203          * occurred.
204          */
205         if ( tlv.valueIndex == Length.UNDEFINED )
206         {
207             needToRead = tlv.length ;
208         }
209         else
210         {
211             needToRead = tlv.length - tlv.valueIndex ;
212         }
213 
214         /*
215          * check if we have the remainder of the value to complete the
216          * TLV within the current buffer - if so we read all of it
217          */
218         if ( buf.remaining() >= needToRead )
219         {
220             tlv.valueChunk = ( ByteBuffer ) buf.slice().limit( needToRead ) ;
221             buf.position( buf.position() + needToRead ) ;
222             tlv.valueIndex = tlv.length ;
223             tlv.index += tlv.length ;
224 
225             cb.partialValueDecoded( tlv ) ;
226             fireDecodeOccurred( tlv ) ;
227             updateStack( needToRead ) ;
228             tlv.clear() ;
229             state = BERDecoderState.TAG ;
230         }
231 
232         /*
233          * the buffer does not contain the rest of the value we need in order
234          * to complete the current TLV - the value is fragmented so we read
235          * what we can and update indices by that amount.
236          */
237         else
238         {
239             if ( tlv.valueIndex == Length.UNDEFINED )
240             {
241                 tlv.valueIndex = 0 ;
242             }
243 
244             int remaining = buf.remaining() ;
245             tlv.valueChunk = buf.slice() ;
246             buf.position( buf.limit() ) ;
247             tlv.valueIndex += remaining ;
248             tlv.index +=remaining ;
249 
250             cb.partialValueDecoded( tlv ) ;
251             updateStack( remaining ) ;
252         }
253     }
254 
255 
256 
257     /* (non-Javadoc)
258      * @see org.apache.asn1.codec.stateful.DecoderCallback#decodeOccurred(
259      * org.apache.asn1.codec.stateful.StatefulDecoder, java.lang.Object)
260      */
261     public void decodeOccurred( StatefulDecoder decoder, Object decoded )
262     {
263         if ( decoder == tagDecoder )
264         {
265             Tag tag = ( Tag ) decoded ;
266             tlv.rawTag = tag.getRawTag() ;
267             tlv.id = tag.getId() ;
268             tlv.isPrimitive = tag.isPrimitive() ;
269             tlv.typeClass = tag.getTypeClass() ;
270             tlv.index = tag.size() ;
271 
272             if ( ! tlv.isIndefiniteTerminator() )
273             {
274                 fireTagDecoded() ;
275                 updateStack( tag.size() ) ;
276             }
277 
278             state = state.getNext( tag.isPrimitive() ) ;
279         }
280         else if ( decoder == lengthDecoder )
281         {
282             Length length = ( Length ) decoded ;
283             tlv.length = length.getLength() ;
284 
285             if ( tlv.length == Length.INDEFINITE )
286             {
287                 tlv.index = Length.INDEFINITE ;
288                 tlv.valueIndex = Length.INDEFINITE ;
289             }
290             else
291             {
292                 tlv.index += length.size() ;
293             }
294 
295             if ( ! tlv.isIndefiniteTerminator() )
296             {
297                 fireLengthDecoded() ;
298             }
299             updateStack( length.size() ) ;
300 
301             if ( ! tlv.isPrimitive )
302             {
303                 if ( tlv.isIndefinite() || tlv.length > 0 )
304                 {
305                     tlvStack.push( tlv.clone() ) ;
306                 }
307                 else
308                 {
309                     state = BERDecoderState.VALUE ;
310                     fireDecodeOccurred( tlv ) ;
311                 }
312 
313                 state = BERDecoderState.TAG ;
314                 tlv.clear() ;
315             }
316             else if ( tlv.isIndefiniteTerminator() )
317             {
318                 return ;
319             }
320             else if ( tlv.length > 0 )
321             {
322                 state = BERDecoderState.VALUE ;
323             }
324             else
325             {
326                 state = BERDecoderState.VALUE ;
327                 tlv.valueChunk = EMPTY_BUFFER ;
328                 cb.partialValueDecoded( tlv ) ;
329                 fireDecodeOccurred( tlv ) ;
330                 state = BERDecoderState.TAG ;
331             }
332         }
333         else
334         {
335             throw new IllegalArgumentException( "unrecognized decoder" ) ;
336         }
337     }
338 
339 
340     // ------------------------------------------------------------------------
341     // private utility methods
342     // ------------------------------------------------------------------------
343 
344 
345     /***
346      * Fires a tag decoded event by making the appropriate calls to the
347      * callback and the monitor.   If the monitor is a BERDecoderMonitor with
348      * extended reporting, then those methods are invoked.
349      *
350      * Also as a side-effect this method clears the tag buffer once it has
351      * finished notifying the monitor and calling the callback.
352      */
353     private void fireTagDecoded()
354     {
355         if ( cb != null )
356         {
357             cb.tagDecoded( tlv ) ;
358         }
359 
360         if ( monitor != null && monitor instanceof BERDecoderMonitor )
361         {
362             BERDecoderMonitor berMonitor = ( BERDecoderMonitor ) monitor ;
363             berMonitor.tagDecoded( tlv ) ;
364         }
365     }
366 
367 
368     /***
369      * Fires a length decoded event by making the appropriate calls to the
370      * callback and the monitor.   If the monitor is a BERDecoderMonitor with
371      * extended reporting, then those methods are invoked.
372      *
373      * Also as a side-effect this method clears the length buffer once it has
374      * finished notifying the monitor and calling the callback.
375      */
376     private void fireLengthDecoded()
377     {
378         if ( cb != null )
379         {
380             cb.lengthDecoded( tlv ) ;
381         }
382 
383         if ( monitor != null && monitor instanceof BERDecoderMonitor )
384         {
385             BERDecoderMonitor berMonitor = ( BERDecoderMonitor ) monitor ;
386             berMonitor.lengthDecoded( tlv ) ;
387         }
388     }
389 
390 
391     /***
392      * Fires a complete TLV decoded event by making the appropriate calls to
393      * the callback and the monitor.
394      */
395     private void fireDecodeOccurred( Tuple tlv )
396     {
397         if ( cb != null )
398         {
399             cb.decodeOccurred( this, tlv ) ;
400         }
401 
402         if ( monitor != null )
403         {
404             monitor.callbackOccured( this, cb, tlv ) ;
405         }
406     }
407 
408 
409     /***
410      * Increments the indices of constructed TLV's within the TLV Stack.
411      *
412      * @param increment the amount to increment indices by.
413      */
414     private void updateStack( int increment )
415     {
416         for ( int ii = 0; ii < tlvStack.size(); ii++ )
417         {
418             Tuple t = ( Tuple ) tlvStack.get( ii ) ;
419 
420             if ( t.isIndefinite() )
421             {
422                 continue ;
423             }
424 
425             t.index += increment ;
426 
427             if ( t.valueIndex == Length.UNDEFINED )
428             {
429                 t.valueIndex = 0 ;
430             }
431 
432             t.valueIndex += increment ;
433         }
434 
435         if ( tlvStack.isEmpty() )
436         {
437             return ;
438         }
439 
440         do
441         {
442             Tuple top = ( Tuple ) tlvStack.peek() ;
443 
444             if ( top.isIndefinite() && tlv.isIndefiniteTerminator() )
445             {
446                 tlvStack.pop() ;
447                 state = BERDecoderState.VALUE ;
448                 fireDecodeOccurred( top ) ;
449                 state = BERDecoderState.TAG ;
450                 break;
451             }
452             else if ( top.isIndefinite() )
453             {
454                 break ;
455             }
456             else if ( top.valueIndex >= top.length )
457             {
458                 tlvStack.pop() ;
459                 state = BERDecoderState.VALUE ;
460                 fireDecodeOccurred( top ) ;
461                 state = BERDecoderState.TAG ;
462             }
463             else
464             {
465                 break ;
466             }
467 
468         } while( tlvStack.size() > 0 ) ;
469     }
470 
471 
472     /*
473 
474      Why copy the raw tag here when we can maintain our own stack in the
475      digester that does the pushing and popping instead?  Keep this here
476      until we decide what to do.
477 
478     public int[] getTagNestingPattern()
479     {
480         int stackSz = tlvStack.size() ;
481         int[] pattern = new int[stackSz+1] ;
482         pattern[stackSz] = tlv.rawTag ;
483 
484         for ( int ii = 0; ii < stackSz; ii++ )
485         {
486             pattern[ii] = ( ( Tuple ) tlvStack.get( ii ) ).rawTag ;
487         }
488 
489         return pattern ;
490     }
491     */
492 
493 
494     // ------------------------------------------------------------------------
495     // Methods used for testing
496     // ------------------------------------------------------------------------
497 
498 
499     /***
500      * Gets the current state of this BERDecoder.  Used only for debugging and
501      * testing.
502      *
503      * @return the state enum
504      */
505     BERDecoderState getState()
506     {
507         return state ;
508     }
509 
510 
511     /***
512      * Gets a cloned copy of the current tuple.  Used only for debugging and
513      * testing.
514      *
515      * @return a clone of the current tlv
516      */
517     Tuple getCurrentTuple()
518     {
519         return ( Tuple ) tlv.clone() ;
520     }
521 
522 
523     /***
524      * Gets a deep copy of the constructed tuple stack.  Used only for debugging
525      * and testing.
526      *
527      * @return a deep copy of the tuple stack
528      */
529     Stack getTupleStack()
530     {
531         Stack stack = new Stack() ;
532 
533         for ( int ii = 0; ii < tlvStack.size(); ii++ )
534         {
535             Tuple t = ( Tuple ) tlvStack.get( ii ) ;
536             stack.add( t.clone() ) ;
537         }
538 
539         return stack ;
540     }
541 }