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.net.Inet6Address;
024import java.net.InetAddress;
025import java.net.UnknownHostException;
026import java.nio.BufferOverflowException;
027import java.nio.ByteBuffer;
028import java.util.Arrays;
029
030import org.apache.directory.api.asn1.Asn1Object;
031import org.apache.directory.api.asn1.EncoderException;
032import org.apache.directory.api.asn1.ber.tlv.BerValue;
033import org.apache.directory.api.asn1.ber.tlv.TLV;
034import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
035import org.apache.directory.api.util.Strings;
036import org.apache.directory.server.i18n.I18n;
037import org.apache.directory.shared.kerberos.codec.types.HostAddrType;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041
042/**
043 * Provides host address information.
044 * 
045 * The ASN.1 grammar for this structure is :
046 * <pre>
047 * HostAddress     ::= SEQUENCE  {
048 *        addr-type       [0] Int32,
049 *        address         [1] OCTET STRING
050 * }
051 * </pre>
052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053 */
054public class HostAddress implements Asn1Object
055{
056    /** The logger */
057    private static final Logger LOG = LoggerFactory.getLogger( HostAddress.class );
058
059    /** Speedup for logs */
060    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
061
062    /** The host address type. One of :
063     *    Address Type                   Value
064     *
065     *    IPv4                             2
066     *    Directional                      3
067     *    ChaosNet                         5
068     *    XNS                              6
069     *    ISO                              7
070     *    DECNET Phase IV                 12
071     *    AppleTalk DDP                   16
072     *    NetBios                         20
073     *    IPv6                            24
074     */
075    private HostAddrType addrType;
076
077    /** The address */
078    private byte[] address;
079
080    // Storage for computed lengths
081    private int addrTypeLength;
082    private int addressLength;
083    private int hostAddressLength;
084    private int hostAddressSeqLength;
085
086
087    /**
088     * Creates an empty HostAdress instance
089     */
090    public HostAddress()
091    {
092    }
093
094
095    /**
096     * Creates a new instance of HostAddress.
097     *
098     * @param addrType The type of address
099     * @param address The address
100     */
101    public HostAddress( HostAddrType addrType, byte[] address )
102    {
103        this.addrType = addrType;
104        this.address = address;
105    }
106
107
108    /**
109     * Creates a new instance of HostAddress.
110     *
111     * @param internetAddress The Inet form address
112     */
113    public HostAddress( InetAddress internetAddress )
114    {
115        addrType = ( internetAddress instanceof Inet6Address ) ? HostAddrType.ADDRTYPE_INET6 : HostAddrType.ADDRTYPE_INET;
116        byte[] newAddress = internetAddress.getAddress();
117        address = new byte[newAddress.length];
118        System.arraycopy( newAddress, 0, address, 0, newAddress.length );
119    }
120
121
122    /**
123     * {@inheritDoc}
124     */
125    @Override
126    public int hashCode()
127    {
128        int hash = 37;
129        hash = hash * 17 + addrType.hashCode();
130
131        if ( address != null )
132        {
133            hash = hash * 17 + Arrays.hashCode( address );
134        }
135
136        return hash;
137    }
138
139
140    /**
141     * Returns whether one {@link HostAddress} is equal to another.
142     *
143     * @param that The {@link HostAddress} to compare with
144     * @return true if the {@link HostAddress}'s are equal.
145     */
146    @Override
147    public boolean equals( Object that )
148    {
149        if ( this == that )
150        {
151            return true;
152        }
153
154        if ( !( that instanceof HostAddress ) )
155        {
156            return false;
157        }
158
159        HostAddress hostAddress = ( HostAddress ) that;
160
161        if ( addrType != hostAddress.addrType || ( address != null && hostAddress.address == null )
162            || ( address == null && hostAddress.address != null ) )
163        {
164            return false;
165        }
166
167        if ( address != null && hostAddress.address != null )
168        {
169            if ( address.length != hostAddress.address.length )
170            {
171                return false;
172            }
173
174            for ( int ii = 0; ii < address.length; ii++ )
175            {
176                if ( address[ii] != hostAddress.address[ii] )
177                {
178                    return false;
179                }
180            }
181        }
182
183        return true;
184    }
185
186
187    /**
188     * Get the bytes for this address.
189     *
190     * @return The bytes of this address.
191     */
192    public byte[] getAddress()
193    {
194        return address;
195    }
196
197
198    /**
199     * Set the address 
200     *
201     * @param addresse The address
202     */
203    public void setAddress( byte[] addresse )
204    {
205        this.address = addresse;
206    }
207
208
209    /**
210     * Compute the host address length
211     * <pre>
212     * HostAddress :
213     * 
214     * 0x30 L1 hostAddress sequence
215     *  |
216     *  +--&gt; 0xA0 L2 addrType tag
217     *  |     |
218     *  |     +--&gt; 0x02 L2-1 addrType (int)
219     *  |
220     *  +--&gt; 0xA1 L3 address tag
221     *        |
222     *        +--&gt; 0x04 L3-1 address (OCTET STRING)
223     *        
224     *  where L1 = L2 + length(0xA0) + length(L2) +
225     *             L3 + length(0xA1) + length(L3) 
226     *  and
227     *  L2 = L2-1 + length(0x02) + length( L2-1) 
228     *  L3 = L3-1 + length(0x04) + length( L3-1) 
229     *  </pre>
230     */
231    public int computeLength()
232    {
233        // Compute the keyType. The Length will always be contained in 1 byte
234        addrTypeLength = 1 + 1 + BerValue.getNbBytes( addrType.getValue() );
235        hostAddressLength = 1 + TLV.getNbBytes( addrTypeLength ) + addrTypeLength;
236
237        // Compute the keyValue
238        if ( address == null )
239        {
240            addressLength = 1 + 1;
241        }
242        else
243        {
244            addressLength = 1 + TLV.getNbBytes( address.length ) + address.length;
245        }
246
247        hostAddressLength += 1 + TLV.getNbBytes( addressLength ) + addressLength;
248
249        // Compute the whole sequence length
250        hostAddressSeqLength = 1 + TLV.getNbBytes( hostAddressLength ) + hostAddressLength;
251
252        return hostAddressSeqLength;
253    }
254
255
256    /**
257     * Encode the HostAddress message to a PDU. 
258     * <pre>
259     * HostAddress :
260     * 
261     * 0x30 LL
262     *   0xA0 LL 
263     *     0x02 0x01 addr-type
264     *   0xA1 LL 
265     *     0x04 LL address
266     * </pre>
267     * @param buffer The buffer where to put the PDU. It should have been allocated
268     * before, with the right size.
269     * @return The constructed PDU.
270     */
271    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
272    {
273        if ( buffer == null )
274        {
275            throw new EncoderException( I18n.err( I18n.ERR_148 ) );
276        }
277
278        try
279        {
280            // The HostAddress SEQ Tag
281            buffer.put( UniversalTag.SEQUENCE.getValue() );
282            buffer.put( TLV.getBytes( hostAddressLength ) );
283
284            // The addr-type, first the tag, then the value
285            buffer.put( ( byte ) 0xA0 );
286            buffer.put( TLV.getBytes( addrTypeLength ) );
287            BerValue.encode( buffer, addrType.getValue() );
288
289            // The address, first the tag, then the value
290            buffer.put( ( byte ) 0xA1 );
291            buffer.put( TLV.getBytes( addressLength ) );
292            BerValue.encode( buffer, address );
293        }
294        catch ( BufferOverflowException boe )
295        {
296            LOG.error( I18n.err( I18n.ERR_143, 1 + TLV.getNbBytes( hostAddressLength )
297                + hostAddressLength, buffer.capacity() ) );
298            throw new EncoderException( I18n.err( I18n.ERR_138 ), boe );
299        }
300
301        if ( IS_DEBUG )
302        {
303            LOG.debug( "Checksum encoding : {}", Strings.dumpBytes( buffer.array() ) );
304            LOG.debug( "Checksum initial value : {}", this );
305        }
306
307        return buffer;
308    }
309
310
311    /**
312     * Returns the {@link HostAddrType} of this {@link HostAddress}.
313     *
314     * @return The {@link HostAddrType}.
315     */
316    public HostAddrType getAddrType()
317    {
318        return addrType;
319    }
320
321
322    /**
323     * Set the addr-type field
324     *
325     * @param addrType The address type
326     */
327    public void setAddrType( HostAddrType addrType )
328    {
329        this.addrType = addrType;
330    }
331
332
333    /**
334     * Set the addr-type field
335     *
336     * @param addrType The address type
337     */
338    public void setAddrType( int addrType )
339    {
340        this.addrType = HostAddrType.getTypeByOrdinal( addrType );
341    }
342
343
344    /**
345     * {@inheritDoc}
346     */
347    public String toString()
348    {
349        try
350        {
351            return InetAddress.getByAddress( address ).getHostAddress();
352        }
353        catch ( UnknownHostException uhe )
354        {
355            return "Unknow host : " + Strings.utf8ToString( address );
356        }
357    }
358}