| 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<1..2^16-1></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); |
| } |
| } |
| } |