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  package org.apache.directory.api.asn1.ber;
21  
22  
23  import java.nio.ByteBuffer;
24  
25  import org.apache.directory.api.asn1.DecoderException;
26  import org.apache.directory.api.asn1.ber.tlv.BerValue;
27  import org.apache.directory.api.asn1.ber.tlv.TLV;
28  import org.apache.directory.api.asn1.ber.tlv.TLVBerDecoderMBean;
29  import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
30  import org.apache.directory.api.asn1.util.Asn1StringUtils;
31  import org.apache.directory.api.i18n.I18n;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  
36  /**
37   * A BER TLV Tag component decoder. This decoder instantiate a Tag. The tag
38   * won't be implementations should not copy the handle to the Tag object
39   * delivered but should copy the data if they need it over the long term.
40   * 
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42  */
43  public class Asn1Decoder implements TLVBerDecoderMBean
44  {
45      /** The logger */
46      private static final Logger LOG = LoggerFactory.getLogger( Asn1Decoder.class );
47  
48      /** A speedup for logger */
49      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
50  
51      /** This flag is used to indicate that there are more bytes in the stream */
52      private static final boolean MORE = true;
53  
54      /** This flag is used to indicate that there are no more bytes in the stream */
55      private static final boolean END = false;
56  
57      /** Flag that is used to allow/disallow the indefinite form of Length */
58      private boolean indefiniteLengthAllowed;
59  
60      /** The maximum number of bytes that could be used to encode the Length */
61      private int maxLengthLength;
62  
63      /** The maximum number of bytes that could be used to encode the Tag */
64      private int maxTagLength;
65  
66  
67      /**
68       * A public constructor of an Asn1 Decoder.
69       */
70      public Asn1Decoder()
71      {
72          indefiniteLengthAllowed = false;
73          maxLengthLength = 1;
74          maxTagLength = 1;
75      }
76  
77  
78      /**
79       * Treat the start of a TLV. It reads the tag and get its value.
80       * 
81       * @param stream The ByteBuffer containing the PDU to decode
82       * @param container The container that stores the current state,
83       * the result and other informations.
84       * @return <code>true</code> if there are more bytes to read, <code>false
85       * </code> otherwise
86       */
87      private boolean treatTagStartState( ByteBuffer stream, Asn1Container container )
88      {
89          if ( stream.hasRemaining() )
90          {
91              byte octet = stream.get();
92  
93              TLV tlv = new TLV( container.getNewTlvId() );
94              tlv.setTag( octet );
95  
96              // Store the current TLV in the container.
97              container.setCurrentTLV( tlv );
98  
99              // Create a link between the current TLV with its parent
100             tlv.setParent( container.getParentTLV() );
101 
102             // Switch to the next state, which is the Length decoding
103             container.setState( TLVStateEnum.LENGTH_STATE_START );
104 
105             if ( IS_DEBUG )
106             {
107                 byte tag = container.getCurrentTLV().getTag();
108                 LOG.debug( "Tag {} has been decoded", Asn1StringUtils.dumpByte( tag ) );
109             }
110 
111             return MORE;
112         }
113         else
114         {
115             // The stream has been exhausted
116             return END;
117         }
118     }
119 
120 
121     /**
122      * Dump the current TLV tree
123      * 
124      * @param container The container
125      */
126     private void dumpTLVTree( Asn1Container container )
127     {
128         StringBuilder sb = new StringBuilder();
129         TLV current = container.getCurrentTLV();
130 
131         sb.append( "TLV" ).append( Asn1StringUtils.dumpByte( current.getTag() ) ).append( "(" ).append(
132             current.getExpectedLength() ).append( ")" );
133 
134         current = current.getParent();
135 
136         while ( current != null )
137         {
138             sb.append( "-TLV" ).append( Asn1StringUtils.dumpByte( current.getTag() ) ).append( "(" ).append(
139                 current.getExpectedLength() ).append( ")" );
140             current = current.getParent();
141         }
142 
143         if ( IS_DEBUG )
144         {
145             LOG.debug( "TLV Tree : {}", sb.toString() );
146         }
147     }
148 
149 
150     /**
151      * Check if the TLV tree is fully decoded
152      * 
153      * @param container The container
154      * @return <code>true</code> if the TLV has been decoded
155      */
156     private boolean isTLVDecoded( Asn1Container container )
157     {
158         TLV current = container.getCurrentTLV();
159         TLV parent = current.getParent();
160 
161         while ( parent != null )
162         {
163             if ( parent.getExpectedLength() != 0 )
164             {
165                 return false;
166             }
167 
168             parent = parent.getParent();
169         }
170 
171         BerValue value = current.getValue();
172 
173         if ( ( value != null ) && ( value.getData() != null ) )
174         {
175             return current.getExpectedLength() == value.getData().length;
176         }
177         else
178         {
179             return current.getExpectedLength() == 0;
180         }
181     }
182 
183 
184     /**
185      * Treat the Length start. The tag has been decoded, so we have to deal with
186      * the LENGTH, which can be multi-bytes.
187      * 
188      * @param stream  The ByteBuffer containing the PDU to decode
189      * @param container The container that stores the current state,
190      * the result and other informations.
191      * @return <code>true</code> if there are more bytes to read, <code>false
192      * </code> otherwise
193      * @throws DecoderException Thrown if anything went wrong
194      */
195     private boolean treatLengthStartState( ByteBuffer stream, Asn1Container container ) throws DecoderException
196     {
197         if ( stream.hasRemaining() )
198         {
199             byte octet = stream.get();
200             TLV tlv = container.getCurrentTLV();
201 
202             if ( ( octet & TLV.LENGTH_LONG_FORM ) == 0 )
203             {
204                 // We don't have a long form. The Length of the Value part is
205                 // given by this byte.
206                 tlv.setLength( octet );
207                 tlv.setLengthNbBytes( 1 );
208 
209                 container.setState( TLVStateEnum.LENGTH_STATE_END );
210             }
211             else if ( ( octet & TLV.LENGTH_EXTENSION_RESERVED ) != TLV.LENGTH_EXTENSION_RESERVED )
212             {
213                 int expectedLength = octet & TLV.LENGTH_SHORT_MASK;
214 
215                 if ( expectedLength > 4 )
216                 {
217                     String msg = I18n.err( I18n.ERR_00005_LENGTH_OVERFLOW );
218                     LOG.error( msg );
219                     throw new DecoderException( msg );
220                 }
221 
222                 tlv.setLength( 0 );
223                 tlv.setLengthNbBytes( 1 + expectedLength );
224                 tlv.setLengthBytesRead( 1 );
225                 container.setState( TLVStateEnum.LENGTH_STATE_PENDING );
226             }
227             else
228             {
229                 String msg = I18n.err( I18n.ERR_00006_LENGTH_EXTENSION_RESERVED );
230                 LOG.error( msg );
231                 throw new DecoderException( msg );
232             }
233 
234             return MORE;
235         }
236         else
237         {
238             return END;
239         }
240     }
241 
242 
243     /**
244      * This function is called when a Length is in the process of being decoded,
245      * but the lack of bytes in the buffer stopped the process.
246      * 
247      * @param stream The ByteBuffer containing the PDU to decode
248      * @param container The container that stores the current state,
249      * the result and other informations.
250      * @return <code>true</code> if there are more bytes to read, <code>false
251      * </code> otherwise
252      */
253     private boolean treatLengthPendingState( ByteBuffer stream, Asn1Container container )
254     {
255         if ( stream.hasRemaining() )
256         {
257             TLV tlv = container.getCurrentTLV();
258             int length = tlv.getLength();
259 
260             while ( tlv.getLengthBytesRead() < tlv.getLengthNbBytes() )
261             {
262                 byte octet = stream.get();
263 
264                 if ( IS_DEBUG )
265                 {
266                     LOG.debug( "  current byte : {}", Asn1StringUtils.dumpByte( octet ) );
267                 }
268 
269                 tlv.incLengthBytesRead();
270                 length = ( length << 8 ) | ( octet & 0x00FF );
271 
272                 if ( !stream.hasRemaining() )
273                 {
274                     tlv.setLength( length );
275 
276                     if ( tlv.getLengthBytesRead() < tlv.getLengthNbBytes() )
277                     {
278                         container.setState( TLVStateEnum.LENGTH_STATE_PENDING );
279                         return END;
280                     }
281                     else
282                     {
283                         container.setState( TLVStateEnum.LENGTH_STATE_END );
284                         return MORE;
285                     }
286                 }
287             }
288 
289             tlv.setLength( length );
290             container.setState( TLVStateEnum.LENGTH_STATE_END );
291 
292             return MORE;
293         }
294         else
295         {
296 
297             return END;
298         }
299     }
300 
301 
302     /**
303      * A debug function used to dump the expected length stack.
304      * 
305      * @param tlv The current TLV.
306      * @return A string which represent the expected length stack.
307      */
308     private String getParentLength( TLV tlv )
309     {
310         StringBuilder buffer = new StringBuilder();
311 
312         buffer.append( "TLV expected length stack : " );
313 
314         while ( true )
315         {
316             if ( tlv == null )
317             {
318                 buffer.append( " - null" );
319                 break;
320             }
321             else
322             {
323                 buffer.append( " - " ).append( tlv.getExpectedLength() );
324             }
325 
326             tlv = tlv.getParent();
327         }
328 
329         return buffer.toString();
330     }
331 
332 
333     /**
334      * The Length is fully decoded. We have to call an action to check the size.
335      * 
336      * @param container The container that stores the current state,
337      * the result and other informations.
338      * @throws DecoderException Thrown if anything went wrong
339      */
340     private void treatLengthEndState( Asn1Container container ) throws DecoderException
341     {
342         TLV tlv = container.getCurrentTLV();
343 
344         if ( tlv == null )
345         {
346             String msg = I18n.err( I18n.ERR_00007_TLV_NULL );
347             LOG.error( msg );
348             throw new DecoderException( msg );
349         }
350 
351         int length = tlv.getLength();
352 
353         // We will check the length here. What we must control is
354         // that the enclosing constructed TLV expected length is not
355         // exceeded by the current TLV.
356         TLV parentTLV = container.getParentTLV();
357 
358         if ( IS_DEBUG )
359         {
360             LOG.debug( "Parent length : {}", getParentLength( parentTLV ) );
361         }
362 
363         if ( parentTLV == null )
364         {
365             // This is the first TLV, so we can't check anything. We will
366             // just store this TLV as the root of the PDU
367             tlv.setExpectedLength( length );
368             container.setParentTLV( tlv );
369 
370             if ( IS_DEBUG )
371             {
372                 LOG.debug( "Root TLV[{}]", Integer.valueOf( length ) );
373             }
374         }
375         else
376         {
377             // We have a parent, so we will check that its expected length is
378             // not exceeded.
379             int expectedLength = parentTLV.getExpectedLength();
380             int currentLength = tlv.getSize();
381 
382             if ( expectedLength < currentLength )
383             {
384                 // The expected length is lower than the Value length of the
385                 // current TLV. This is an error...
386                 LOG.debug( "tlv[{}, {}]", Integer.valueOf( expectedLength ), Integer.valueOf( currentLength ) );
387                 throw new DecoderException( I18n.err( I18n.ERR_00008_VALUE_LENGTH_ABOVE_EXPECTED_LENGTH, Integer
388                     .valueOf( currentLength ), Integer.valueOf( expectedLength ) ) );
389             }
390 
391             // deal with the particular case where expected length equal
392             // the current length, which means that the parentTLV has been
393             // completed.
394             if ( expectedLength == currentLength )
395             {
396                 parentTLV.setExpectedLength( 0 );
397 
398                 // We also have to check that the current TLV is a constructed
399                 // one.
400                 // In this case, we have to switch from this parent TLV
401                 // to the parent's parent TLV.
402                 if ( tlv.isConstructed() )
403                 {
404                     // here, we also have another special case : a
405                     // zero length TLV. We must then unstack all
406                     // the parents which length is null.
407                     if ( length == 0 )
408                     {
409                         // We will set the parent to the first parentTLV which
410                         // expectedLength
411                         // is not null, and it will become the new parent TLV
412                         while ( parentTLV != null )
413                         {
414                             if ( parentTLV.getExpectedLength() != 0 )
415                             {
416                                 // ok, we have an incomplete parent. we will
417                                 // stop the recursion right here
418                                 break;
419                             }
420                             else
421                             {
422                                 parentTLV = parentTLV.getParent();
423                             }
424                         }
425 
426                         container.setParentTLV( parentTLV );
427                     }
428                     else
429                     {
430                         // The new Parent TLV is this Constructed TLV
431                         container.setParentTLV( tlv );
432                     }
433 
434                     tlv.setParent( parentTLV );
435                     tlv.setExpectedLength( length );
436                 }
437                 else
438                 {
439                     tlv.setExpectedLength( length );
440 
441                     // It's over, the parent TLV has been completed.
442                     // Go back to the parent's parent TLV until we find
443                     // a tlv which is not complete.
444                     while ( parentTLV != null )
445                     {
446                         if ( parentTLV.getExpectedLength() != 0 )
447                         {
448                             // ok, we have an incomplete parent. we will
449                             // stop the recursion right here
450                             break;
451                         }
452                         else
453                         {
454                             parentTLV = parentTLV.getParent();
455                         }
456                     }
457 
458                     container.setParentTLV( parentTLV );
459                 }
460             }
461             else
462             {
463                 // Renew the expected Length.
464                 parentTLV.setExpectedLength( expectedLength - currentLength );
465                 tlv.setExpectedLength( length );
466 
467                 if ( tlv.isConstructed() )
468                 {
469                     // We have a constructed tag, so we must switch the
470                     // parentTLV
471                     tlv.setParent( parentTLV );
472                     container.setParentTLV( tlv );
473                 }
474             }
475 
476         }
477 
478         if ( IS_DEBUG )
479         {
480             LOG.debug( "Length {} has been decoded", Integer.valueOf( length ) );
481         }
482 
483         if ( length == 0 )
484         {
485             // The length is 0, so we can't expect a value.
486             container.setState( TLVStateEnum.TLV_STATE_DONE );
487         }
488         else
489         {
490             // Go ahead and decode the value part
491             container.setState( TLVStateEnum.VALUE_STATE_START );
492         }
493     }
494 
495 
496     /**
497      * Treat the Value part. We will distinguish two cases : - if the Tag is a
498      * Primitive one, we will get the value. - if the Tag is a Constructed one,
499      * nothing will be done.
500      * 
501      * @param stream The ByteBuffer containing the PDU to decode
502      * @param container The container that stores the current state,
503      * the result and other informations.
504      * @return <code>true</code> if there are more bytes to read, <code>false
505      * </code> otherwise
506      */
507     private boolean treatValueStartState( ByteBuffer stream, Asn1Container container )
508     {
509         TLV currentTlv = container.getCurrentTLV();
510 
511         if ( TLV.isConstructed( currentTlv.getTag() ) && !container.isGathering() )
512         {
513             container.setState( TLVStateEnum.TLV_STATE_DONE );
514 
515             return MORE;
516         }
517         else
518         {
519             int length = currentTlv.getLength();
520             int nbBytes = stream.remaining();
521 
522             if ( nbBytes < length )
523             {
524                 currentTlv.getValue().init( length );
525                 currentTlv.getValue().setData( stream );
526                 container.setState( TLVStateEnum.VALUE_STATE_PENDING );
527 
528                 return END;
529             }
530             else
531             {
532                 currentTlv.getValue().init( length );
533                 stream.get( currentTlv.getValue().getData(), 0, length );
534                 container.setState( TLVStateEnum.TLV_STATE_DONE );
535 
536                 return MORE;
537             }
538         }
539     }
540 
541 
542     /**
543      * Treat a pending Value when we get more bytes in the buffer.
544      * 
545      * @param stream The ByteBuffer containing the PDU to decode
546      * @param container The container that stores the current state,
547      * the result and other informations.
548      * @return <code>MORE</code> if some bytes remain in the buffer when the
549      * value has been decoded, <code>END</code> if whe still need to get some
550      * more bytes.
551      */
552     private boolean treatValuePendingState( ByteBuffer stream, Asn1Container container )
553     {
554         TLV currentTlv = container.getCurrentTLV();
555 
556         int length = currentTlv.getLength();
557         int currentLength = currentTlv.getValue().getCurrentLength();
558         int nbBytes = stream.remaining();
559 
560         if ( ( currentLength + nbBytes ) < length )
561         {
562             currentTlv.getValue().addData( stream );
563             container.setState( TLVStateEnum.VALUE_STATE_PENDING );
564 
565             return END;
566         }
567         else
568         {
569             int remaining = length - currentLength;
570             byte[] data = new byte[remaining];
571             stream.get( data, 0, remaining );
572             currentTlv.getValue().addData( data );
573             container.setState( TLVStateEnum.TLV_STATE_DONE );
574 
575             return MORE;
576         }
577     }
578 
579 
580     /**
581      * When the TLV has been fully decoded, we have to execute the associated
582      * action and switch to the next TLV, which will start with a Tag.
583      * 
584      * @param stream The ByteBuffer containing the PDU to decode
585      * @param container The container that stores the current state,
586      * the result and other informations.
587      * @return <code>true</code> if there are more bytes to read, <code>false
588      * </code> otherwise
589      * @throws DecoderException Thrown if anything went wrong
590      */
591     @SuppressWarnings("unchecked")
592     private boolean treatTLVDoneState( ByteBuffer stream, Asn1Container container ) throws DecoderException
593     {
594         if ( IS_DEBUG )
595         {
596             dumpTLVTree( container );
597         }
598 
599         // First, we have to execute the associated action
600         container.getGrammar().executeAction( container );
601 
602         // Check if the PDU has been fully decoded.
603         if ( isTLVDecoded( container ) )
604         {
605             if ( container.getState() == TLVStateEnum.GRAMMAR_END )
606             {
607                 // Change the state to DECODED
608                 container.setState( TLVStateEnum.PDU_DECODED );
609             }
610             else
611             {
612                 if ( container.isGrammarEndAllowed() )
613                 {
614                     // Change the state to DECODED
615                     container.setState( TLVStateEnum.PDU_DECODED );
616                 }
617                 else
618                 {
619                     LOG.error( I18n.err( I18n.ERR_00009_MORE_TLV_EXPECTED ) );
620                     throw new DecoderException( I18n.err( I18n.ERR_00010_TRUNCATED_PDU ) );
621                 }
622             }
623         }
624         else
625         {
626             // Then we switch to the Start tag state and free the current TLV
627             container.setState( TLVStateEnum.TAG_STATE_START );
628         }
629 
630         return stream.hasRemaining();
631     }
632 
633 
634     /**
635      * The decoder main function. This is where we read bytes from the stream
636      * and go through the automaton. It's an inifnite loop which stop when no
637      * more bytes are to be read. It can occurs if the ByteBuffer is exhausted
638      * or if the PDU has been fully decoded.
639      * 
640      * @param stream The ByteBuffer containing the PDU to decode
641      * @param container The container that store the state, the result
642      * and other elements.
643      * @throws DecoderException Thrown if anything went wrong!
644      */
645     public void decode( ByteBuffer stream, Asn1Container container ) throws DecoderException
646     {
647         /*
648          * We have to deal with the current state. This is an infinite loop,
649          * which will stop for any of these reasons :
650          * - STATE_END has been reached (hopefully, the most frequent case)
651          * - buffer is empty (it could happen)
652          * - STATE_OVERFLOW : bad situation ! The PDU may be a
653          * malevolous hand crafted ones, that try to "kill" our decoder. We
654          * must log it with all information to track back this case, and punish
655          * the guilty !
656          */
657         boolean hasRemaining = stream.hasRemaining();
658 
659         // Increment the PDU size counter.
660         container.incrementDecodedBytes( stream.remaining() );
661 
662         if ( container.getDecodedBytes() > container.getMaxPDUSize() )
663         {
664             String message = I18n.err( I18n.ERR_00042_PDU_SIZE_TOO_LONG, container.getDecodedBytes(), container
665                 .getMaxPDUSize() );
666             LOG.error( message );
667             throw new DecoderException( message );
668         }
669 
670         if ( IS_DEBUG )
671         {
672             LOG.debug( ">>>==========================================" );
673             LOG.debug( "--> Decoding a PDU" );
674             LOG.debug( ">>>------------------------------------------" );
675         }
676 
677         while ( hasRemaining )
678         {
679             if ( IS_DEBUG )
680             {
681                 LOG.debug( "--- State = {} ---", container.getState() );
682 
683                 if ( stream.hasRemaining() )
684                 {
685                     byte octet = stream.get( stream.position() );
686 
687                     LOG.debug( "  current byte : {}", Asn1StringUtils.dumpByte( octet ) );
688                 }
689                 else
690                 {
691                     LOG.debug( "  no more byte to decode in the stream" );
692                 }
693             }
694 
695             switch ( container.getState() )
696             {
697                 case TAG_STATE_START:
698                     // Reset the GrammarEnd flag first
699                     container.setGrammarEndAllowed( false );
700                     hasRemaining = treatTagStartState( stream, container );
701 
702                     break;
703 
704                 case LENGTH_STATE_START:
705                     hasRemaining = treatLengthStartState( stream, container );
706 
707                     break;
708 
709                 case LENGTH_STATE_PENDING:
710                     hasRemaining = treatLengthPendingState( stream, container );
711 
712                     break;
713 
714                 case LENGTH_STATE_END:
715                     treatLengthEndState( container );
716 
717                     break;
718 
719                 case VALUE_STATE_START:
720                     hasRemaining = treatValueStartState( stream, container );
721 
722                     break;
723 
724                 case VALUE_STATE_PENDING:
725                     hasRemaining = treatValuePendingState( stream, container );
726 
727                     break;
728 
729                 case VALUE_STATE_END:
730                     hasRemaining = stream.hasRemaining();
731 
732                     // Nothing to do. We will never reach this state
733                     break;
734 
735                 case TLV_STATE_DONE:
736                     hasRemaining = treatTLVDoneState( stream, container );
737 
738                     break;
739 
740                 case PDU_DECODED:
741                     // We have to deal with the case where there are
742                     // more bytes in the buffer, but the PDU has been decoded.
743                     if ( LOG.isDebugEnabled() )
744                     {
745                         LOG.debug( I18n.err( I18n.ERR_00043_REMAINING_BYTES_FOR_DECODED_PDU ) );
746                     }
747 
748                     hasRemaining = false;
749 
750                     break;
751 
752                 default:
753                     break;
754             }
755         }
756 
757         if ( IS_DEBUG )
758         {
759             LOG.debug( "<<<------------------------------------------" );
760 
761             if ( container.getState() == TLVStateEnum.PDU_DECODED )
762             {
763                 if ( container.getCurrentTLV() != null )
764                 {
765                     LOG.debug( "<-- Stop decoding : {}", container.getCurrentTLV().toString() );
766                 }
767                 else
768                 {
769                     LOG.debug( "<-- Stop decoding : null current TLV" );
770                 }
771             }
772             else
773             {
774                 if ( container.getCurrentTLV() != null )
775                 {
776                     LOG.debug( "<-- End decoding : {}", container.getCurrentTLV().toString() );
777                 }
778                 else
779                 {
780                     LOG.debug( "<-- End decoding : null current TLV" );
781                 }
782             }
783 
784             LOG.debug( "<<<==========================================" );
785         }
786     }
787 
788 
789     /**
790      * {@inheritDoc}
791      */
792     @Override
793     public int getMaxLengthLength()
794     {
795         return maxLengthLength;
796     }
797 
798 
799     /**
800      * {@inheritDoc}
801      */
802     @Override
803     public int getMaxTagLength()
804     {
805         return maxTagLength;
806     }
807 
808 
809     /**
810      * {@inheritDoc}
811      */
812     @Override
813     public void disallowIndefiniteLength()
814     {
815         this.indefiniteLengthAllowed = false;
816     }
817 
818 
819     /**
820      * {@inheritDoc}
821      */
822     @Override
823     public void allowIndefiniteLength()
824     {
825         this.indefiniteLengthAllowed = true;
826     }
827 
828 
829     /**
830      * {@inheritDoc}
831      */
832     @Override
833     public boolean isIndefiniteLengthAllowed()
834     {
835 
836         return indefiniteLengthAllowed;
837     }
838 
839 
840     /**
841      * {@inheritDoc}
842      */
843     @Override
844     public void setMaxLengthLength( int maxLengthLength ) throws DecoderException
845     {
846         if ( ( this.indefiniteLengthAllowed ) && ( maxLengthLength > 126 ) )
847         {
848             throw new DecoderException( I18n.err( I18n.ERR_00011_LENGTH_TOO_LONG_FOR_DEFINITE_FORM ) );
849         }
850 
851         this.maxLengthLength = maxLengthLength;
852     }
853 
854 
855     /**
856      * {@inheritDoc}
857      */
858     @Override
859     public void setMaxTagLength( int maxTagLength )
860     {
861         this.maxTagLength = maxTagLength;
862     }
863 }