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 */
020package org.apache.directory.shared.kerberos.components;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025import java.util.ArrayList;
026import java.util.List;
027
028import org.apache.directory.api.asn1.Asn1Object;
029import org.apache.directory.api.asn1.EncoderException;
030import org.apache.directory.api.asn1.ber.tlv.BerValue;
031import org.apache.directory.api.asn1.ber.tlv.TLV;
032import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
033import org.apache.directory.api.util.Strings;
034import org.apache.directory.server.i18n.I18n;
035import org.apache.directory.shared.kerberos.KerberosConstants;
036import org.apache.directory.shared.kerberos.KerberosTime;
037import org.apache.directory.shared.kerberos.codec.types.LastReqType;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041
042/**
043 * The LastReq structure.
044 * 
045 * The ASN.1 grammar is :
046 * <pre>
047 * LastReq         ::=     SEQUENCE OF SEQUENCE {
048 *         lr-type         [0] Int32,
049 *         lr-value        [1] KerberosTime
050 * }
051 * </pre>
052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053 */
054public class LastReq implements Asn1Object
055{
056    /** The logger */
057    private static final Logger LOG = LoggerFactory.getLogger( LastReq.class );
058
059    /** Speedup for logs */
060    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
061
062    /** The list of LastReq elements */
063    private List<LastReqEntry> lastReqs = new ArrayList<>();
064
065    /** The current LastReqEntry being processed */
066    private LastReqEntry currentLR;
067
068    // Storage for computed lengths
069    private int lrTypeTagLen[];
070    private int lrValueTagLen[];
071    private int lastReqSeqLen[];
072    private int lastReqSeqSeqLen;
073
074
075    /**
076     * Creates a new instance of LastReq.
077     */
078    public LastReq()
079    {
080    }
081
082
083    /**
084     * @return the CurrentLr type
085     */
086    public LastReqType getCurrentLrType()
087    {
088        return currentLR.getLrType();
089    }
090
091
092    /**
093     * Set the CurrentLr type
094     */
095    public void setCurrentLrType( LastReqType lrType )
096    {
097        currentLR.setLrType( lrType );
098    }
099
100
101    /**
102     * @return the CurrentLr value
103     */
104    public KerberosTime getCurrentLrValue()
105    {
106        return currentLR.getLrValue();
107    }
108
109
110    /**
111     * Set the CurrentLr value
112     */
113    public void setCurrentLrValue( KerberosTime lrValue )
114    {
115        currentLR.setLrValue( lrValue );
116    }
117
118
119    /**
120     * @return the CurrentLR
121     */
122    public LastReqEntry getCurrentLR()
123    {
124        return currentLR;
125    }
126
127
128    /**
129     * Create a new currentLR
130     */
131    public void createNewLR()
132    {
133        currentLR = new LastReqEntry();
134        lastReqs.add( currentLR );
135    }
136
137
138    /**
139     * Add a new LastReqEntry
140     * @param lastReqEntry The enry to add
141     */
142    public void addEntry( LastReqEntry lastReqEntry )
143    {
144        lastReqs.add( lastReqEntry );
145    }
146
147
148    /**
149     * @return the LastReqs
150     */
151    public List<LastReqEntry> getLastReqs()
152    {
153        return lastReqs;
154    }
155
156
157    /**
158     * Compute the LastReq length
159     * 
160     * <pre>
161     * LastReq :
162     * 
163     * 0x30 L1 LastReq
164     *  |
165     *  +--&gt; 0x30 L2 
166     *        |
167     *        +--&gt; 0xA0 L3 lr-type tag
168     *        |     |
169     *        |     +--&gt; 0x02 L3-1 lrType (int)
170     *        |
171     *        +--&gt; 0xA1 0x11 lr-value tag
172     *              |
173     *              +--&gt; 0x18 0x0F ttt (KerberosString)
174     *  </pre>
175     */
176    public int computeLength()
177    {
178        int i = 0;
179        lastReqSeqLen = new int[lastReqs.size()];
180        lrTypeTagLen = new int[lastReqs.size()];
181        lrValueTagLen = new int[lastReqs.size()];
182        lastReqSeqSeqLen = 0;
183
184        for ( LastReqEntry lre : lastReqs )
185        {
186            int lrTypeLen = BerValue.getNbBytes( lre.getLrType().getValue() );
187            lrTypeTagLen[i] = 1 + TLV.getNbBytes( lrTypeLen ) + lrTypeLen;
188            byte[] lrValyeBytes = lre.getLrValue().getBytes();
189            lrValueTagLen[i] = 1 + TLV.getNbBytes( lrValyeBytes.length ) + lrValyeBytes.length;
190
191            lastReqSeqLen[i] = 1 + TLV.getNbBytes( lrTypeTagLen[i] ) + lrTypeTagLen[i] +
192                1 + TLV.getNbBytes( lrValueTagLen[i] ) + lrValueTagLen[i];
193
194            lastReqSeqSeqLen += 1 + TLV.getNbBytes( lastReqSeqLen[i] ) + lastReqSeqLen[i];
195            i++;
196        }
197
198        return 1 + TLV.getNbBytes( lastReqSeqSeqLen ) + lastReqSeqSeqLen;
199    }
200
201
202    /**
203     * Encode the LastReq message to a PDU. 
204     * 
205     * <pre>
206     * LastReq :
207     * 
208     * 0x30 LL
209     *   0x30 LL
210     *     0xA0 LL 
211     *       0x02 0x01 lrType
212     *     0xA1 0x11 
213     *       0x18 0x0F lrValue
214     * </pre>
215     * 
216     * @param buffer The buffer where to put the PDU. It should have been allocated
217     * before, with the right size.
218     * @return The constructed PDU.
219     */
220    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
221    {
222        if ( buffer == null )
223        {
224            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
225        }
226
227        try
228        {
229            // The lastRequest SEQ OF Tag
230            buffer.put( UniversalTag.SEQUENCE.getValue() );
231            buffer.put( TLV.getBytes( lastReqSeqSeqLen ) );
232
233            int i = 0;
234
235            for ( LastReqEntry lre : lastReqs )
236            {
237                buffer.put( UniversalTag.SEQUENCE.getValue() );
238                buffer.put( TLV.getBytes( lastReqSeqLen[i] ) );
239
240                // the lrType
241                buffer.put( ( byte ) KerberosConstants.LAST_REQ_LR_TYPE_TAG );
242                buffer.put( TLV.getBytes( lrTypeTagLen[i] ) );
243                BerValue.encode( buffer, lre.getLrType().getValue() );
244
245                // the lrValue tag
246                buffer.put( ( byte ) KerberosConstants.LAST_REQ_LR_VALUE_TAG );
247                buffer.put( TLV.getBytes( lrValueTagLen[i] ) );
248
249                // the lrValue value
250                buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
251                buffer.put( ( byte ) 0x0F );
252                buffer.put( lre.getLrValue().getBytes() );
253            }
254        }
255        catch ( BufferOverflowException boe )
256        {
257            LOG.error( I18n.err( I18n.ERR_139, 1 + TLV.getNbBytes( lastReqSeqSeqLen )
258                + lastReqSeqSeqLen, buffer.capacity() ) );
259            throw new EncoderException( I18n.err( I18n.ERR_138 ) , boe);
260        }
261
262        if ( IS_DEBUG )
263        {
264            LOG.debug( "LastReq encoding : {}", Strings.dumpBytes( buffer.array() ) );
265            LOG.debug( "LastReq initial value : {}", this );
266        }
267
268        return buffer;
269    }
270
271
272    /**
273     * @see Object#toString()
274     */
275    public String toString( String tabs )
276    {
277        StringBuilder sb = new StringBuilder();
278
279        sb.append( tabs ).append( "LastReq : \n" );
280
281        for ( LastReqEntry lre : lastReqs )
282        {
283            sb.append( lre.toString( tabs + "    " ) );
284        }
285
286        return sb.toString();
287    }
288
289
290    /**
291     * @see Object#toString()
292     */
293    public String toString()
294    {
295        return toString( "" );
296    }
297}