blob: d5aaad62155bf73f7d045db1f4229d62481f74cd [file] [log] [blame]
package gnu.javax.net.ssl.provider;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* A list of extensions, that may appear in either the {@link ClientHello} or
* {@link ServerHello}. The form of the extensions list is:
*
* <tt> Extension extensions_list&lt;1..2^16-1&gt;</tt>
*
* @author csm
*/
public class ExtensionList implements Builder, Iterable<Extension>
{
private final ByteBuffer buffer;
private int modCount;
public ExtensionList (ByteBuffer buffer)
{
this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
modCount = 0;
}
public ExtensionList(List<Extension> extensions)
{
int length = 2;
for (Extension extension : extensions)
length += extension.length();
buffer = ByteBuffer.allocate(length);
buffer.putShort((short) (length - 2));
for (Extension extension : extensions)
buffer.put(extension.buffer());
buffer.rewind();
}
public ByteBuffer buffer()
{
return (ByteBuffer) buffer.duplicate().limit(length());
}
public Extension get (final int index)
{
int length = length ();
int i;
int n = 0;
for (i = 2; i < length && n < index; )
{
int l = buffer.getShort (i+2) & 0xFFFF;
i += l + 4;
n++;
}
if (n < index)
throw new IndexOutOfBoundsException ("no elemenet at " + index);
int el = buffer.getShort (i+2) & 0xFFFF;
ByteBuffer b = (ByteBuffer) buffer.duplicate().position(i).limit(i+el+4);
return new Extension(b.slice());
}
/**
* Returns the number of extensions this list contains.
*
* @return The number of extensions.
*/
public int size ()
{
int length = length ();
if (length == 0)
return 0;
int n = 0;
for (int i = 2; i < length; )
{
int len = buffer.getShort (i+2) & 0xFFFF;
i += len + 4;
n++;
}
return n;
}
/**
* Returns the length of this extension list, in bytes.
*
* @return The length of this extension list, in bytes.
*/
public int length ()
{
return (buffer.getShort (0) & 0xFFFF) + 2;
}
/**
* Sets the extension at index <i>i</i> to <i>e</i>. Note that setting an
* element at an index <b>may</b> invalidate any other elements that come
* after element at index <i>i</i>. In other words, no attempt is made to
* move existing elements in this list, and since extensions are variable
* length, you can <em>not</em> guarantee that extensions later in the list
* will still be valid.
*
* <p>Thus, elements of this list <b>must</b> be set in order of increasing
* index.
*
* @param index The index to set the extension at.
* @param e The extension.
* @throws java.nio.BufferOverflowException If setting the extension overflows
* the buffer.
* @throws IllegalArgumentException If it isn't possible to find the given index
* in the current list (say, if no element index - 1 is set), or if setting
* the extension will overflow the current list length (given by {@link
* #length()}).
*/
public void set (final int index, Extension e)
{
int length = length();
int n = 0;
int i;
for (i = 2; i < length && n < index; )
{
int len = buffer.getShort(i+2) & 0xFFFF;
i += len + 4;
n++;
}
if (n < index)
throw new IllegalArgumentException("nothing set at index " + (index-1)
+ " or insufficient space");
if (i + e.length() + 2 > length)
throw new IllegalArgumentException("adding this element will exceed the "
+ "list length");
buffer.putShort(i, (short) e.type().getValue());
buffer.putShort(i+2, (short) e.length());
((ByteBuffer) buffer.duplicate().position(i+4)).put (e.valueBuffer());
modCount++;
}
/**
* Reserve space for an extension at index <i>i</i> in the list. In other
* words, this does the job of {@link #set(int, Extension)}, but does not
* copy the extension value to the underlying buffer.
*
* @param index The index of the extension to reserve space for.
* @param t The type of the extension.
* @param eLength The number of bytes to reserve for this extension. The total
* number of bytes used by this method is this length, plus four.
*/
public void set (final int index, Extension.Type t, final int eLength)
{
int length = length ();
int n = 0;
int i;
for (i = 2; i < length && n < index; )
{
int len = buffer.getShort (i+2) & 0xFFFF;
i += len + 4;
n++;
}
if (n < index)
throw new IllegalArgumentException ("nothing set at index " + (index-1)
+ " or insufficient space");
if (i + eLength + 2 > length)
throw new IllegalArgumentException ("adding this element will exceed the "
+ "list length");
buffer.putShort(i, (short) t.getValue());
buffer.putShort(i+2, (short) eLength);
modCount++;
}
/**
* Set the total length of this list, in bytes.
*
* @param newLength The new list length.
*/
public void setLength (final int newLength)
{
if (newLength < 0 || newLength > 65535)
throw new IllegalArgumentException ("invalid length");
buffer.putShort (0, (short) newLength);
modCount++;
}
public Iterator<Extension> iterator()
{
return new ExtensionsIterator();
}
public String toString()
{
return toString (null);
}
public String toString(final String prefix)
{
StringWriter str = new StringWriter();
PrintWriter out = new PrintWriter(str);
if (prefix != null) out.print(prefix);
out.println("ExtensionList {");
if (prefix != null) out.print(prefix);
out.print(" length = ");
out.print(length());
out.println(";");
String subprefix = " ";
if (prefix != null)
subprefix = prefix + subprefix;
for (Extension e : this)
out.println(e.toString(subprefix));
if (prefix != null) out.print(prefix);
out.print("};");
return str.toString();
}
/**
* List iterator interface to an extensions list.
*
* @author csm@gnu.org
*/
public final class ExtensionsIterator implements ListIterator<Extension>
{
private final int modCount;
private int index;
private final int size;
public ExtensionsIterator ()
{
this.modCount = ExtensionList.this.modCount;
index = 0;
size = size ();
}
public boolean hasNext()
{
return index < size;
}
public boolean hasPrevious()
{
return index > 0;
}
public Extension next() throws NoSuchElementException
{
if (modCount != ExtensionList.this.modCount)
throw new ConcurrentModificationException ();
if (!hasNext ())
throw new NoSuchElementException ();
return get (index++);
}
public Extension previous() throws NoSuchElementException
{
if (modCount != ExtensionList.this.modCount)
throw new ConcurrentModificationException ();
if (!hasPrevious ())
throw new NoSuchElementException ();
return get (--index);
}
public int nextIndex()
{
if (hasNext ())
return index + 1;
return index;
}
public int previousIndex()
{
if (hasPrevious ())
return index - 1;
return -1;
}
public void add(Extension e)
{
throw new UnsupportedOperationException ("cannot add items to this iterator");
}
public void remove()
{
throw new UnsupportedOperationException ("cannot remove items from this iterator");
}
public void set(Extension e)
{
ExtensionList.this.set (index, e);
}
}
}