1 package gnu.javax.net.ssl.provider; 2 3 import java.io.PrintWriter; 4 import java.io.StringWriter; 5 import java.nio.ByteBuffer; 6 import java.nio.ByteOrder; 7 import java.util.ConcurrentModificationException; 8 import java.util.Iterator; 9 import java.util.List; 10 import java.util.ListIterator; 11 import java.util.NoSuchElementException; 12 13 /** 14 * A list of extensions, that may appear in either the {@link ClientHello} or 15 * {@link ServerHello}. The form of the extensions list is: 16 * 17 * <tt> Extension extensions_list<1..2^16-1></tt> 18 * 19 * @author csm 20 */ 21 public class ExtensionList implements Builder, Iterable<Extension> 22 { 23 private final ByteBuffer buffer; 24 private int modCount; 25 ExtensionList(ByteBuffer buffer)26 public ExtensionList (ByteBuffer buffer) 27 { 28 this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); 29 modCount = 0; 30 } 31 ExtensionList(List<Extension> extensions)32 public ExtensionList(List<Extension> extensions) 33 { 34 int length = 2; 35 for (Extension extension : extensions) 36 length += extension.length(); 37 buffer = ByteBuffer.allocate(length); 38 buffer.putShort((short) (length - 2)); 39 for (Extension extension : extensions) 40 buffer.put(extension.buffer()); 41 buffer.rewind(); 42 } 43 buffer()44 public ByteBuffer buffer() 45 { 46 return (ByteBuffer) buffer.duplicate().limit(length()); 47 } 48 get(final int index)49 public Extension get (final int index) 50 { 51 int length = length (); 52 int i; 53 int n = 0; 54 for (i = 2; i < length && n < index; ) 55 { 56 int l = buffer.getShort (i+2) & 0xFFFF; 57 i += l + 4; 58 n++; 59 } 60 if (n < index) 61 throw new IndexOutOfBoundsException ("no elemenet at " + index); 62 int el = buffer.getShort (i+2) & 0xFFFF; 63 ByteBuffer b = (ByteBuffer) buffer.duplicate().position(i).limit(i+el+4); 64 return new Extension(b.slice()); 65 } 66 67 /** 68 * Returns the number of extensions this list contains. 69 * 70 * @return The number of extensions. 71 */ size()72 public int size () 73 { 74 int length = length (); 75 if (length == 0) 76 return 0; 77 int n = 0; 78 for (int i = 2; i < length; ) 79 { 80 int len = buffer.getShort (i+2) & 0xFFFF; 81 i += len + 4; 82 n++; 83 } 84 return n; 85 } 86 87 /** 88 * Returns the length of this extension list, in bytes. 89 * 90 * @return The length of this extension list, in bytes. 91 */ length()92 public int length () 93 { 94 return (buffer.getShort (0) & 0xFFFF) + 2; 95 } 96 97 /** 98 * Sets the extension at index <i>i</i> to <i>e</i>. Note that setting an 99 * element at an index <b>may</b> invalidate any other elements that come 100 * after element at index <i>i</i>. In other words, no attempt is made to 101 * move existing elements in this list, and since extensions are variable 102 * length, you can <em>not</em> guarantee that extensions later in the list 103 * will still be valid. 104 * 105 * <p>Thus, elements of this list <b>must</b> be set in order of increasing 106 * index. 107 * 108 * @param index The index to set the extension at. 109 * @param e The extension. 110 * @throws java.nio.BufferOverflowException If setting the extension overflows 111 * the buffer. 112 * @throws IllegalArgumentException If it isn't possible to find the given index 113 * in the current list (say, if no element index - 1 is set), or if setting 114 * the extension will overflow the current list length (given by {@link 115 * #length()}). 116 */ set(final int index, Extension e)117 public void set (final int index, Extension e) 118 { 119 int length = length(); 120 int n = 0; 121 int i; 122 for (i = 2; i < length && n < index; ) 123 { 124 int len = buffer.getShort(i+2) & 0xFFFF; 125 i += len + 4; 126 n++; 127 } 128 if (n < index) 129 throw new IllegalArgumentException("nothing set at index " + (index-1) 130 + " or insufficient space"); 131 if (i + e.length() + 2 > length) 132 throw new IllegalArgumentException("adding this element will exceed the " 133 + "list length"); 134 buffer.putShort(i, (short) e.type().getValue()); 135 buffer.putShort(i+2, (short) e.length()); 136 ((ByteBuffer) buffer.duplicate().position(i+4)).put (e.valueBuffer()); 137 modCount++; 138 } 139 140 /** 141 * Reserve space for an extension at index <i>i</i> in the list. In other 142 * words, this does the job of {@link #set(int, Extension)}, but does not 143 * copy the extension value to the underlying buffer. 144 * 145 * @param index The index of the extension to reserve space for. 146 * @param t The type of the extension. 147 * @param eLength The number of bytes to reserve for this extension. The total 148 * number of bytes used by this method is this length, plus four. 149 */ set(final int index, Extension.Type t, final int eLength)150 public void set (final int index, Extension.Type t, final int eLength) 151 { 152 int length = length (); 153 int n = 0; 154 int i; 155 for (i = 2; i < length && n < index; ) 156 { 157 int len = buffer.getShort (i+2) & 0xFFFF; 158 i += len + 4; 159 n++; 160 } 161 if (n < index) 162 throw new IllegalArgumentException ("nothing set at index " + (index-1) 163 + " or insufficient space"); 164 if (i + eLength + 2 > length) 165 throw new IllegalArgumentException ("adding this element will exceed the " 166 + "list length"); 167 buffer.putShort(i, (short) t.getValue()); 168 buffer.putShort(i+2, (short) eLength); 169 modCount++; 170 } 171 172 /** 173 * Set the total length of this list, in bytes. 174 * 175 * @param newLength The new list length. 176 */ setLength(final int newLength)177 public void setLength (final int newLength) 178 { 179 if (newLength < 0 || newLength > 65535) 180 throw new IllegalArgumentException ("invalid length"); 181 buffer.putShort (0, (short) newLength); 182 modCount++; 183 } 184 iterator()185 public Iterator<Extension> iterator() 186 { 187 return new ExtensionsIterator(); 188 } 189 toString()190 public String toString() 191 { 192 return toString (null); 193 } 194 toString(final String prefix)195 public String toString(final String prefix) 196 { 197 StringWriter str = new StringWriter(); 198 PrintWriter out = new PrintWriter(str); 199 if (prefix != null) out.print(prefix); 200 out.println("ExtensionList {"); 201 if (prefix != null) out.print(prefix); 202 out.print(" length = "); 203 out.print(length()); 204 out.println(";"); 205 String subprefix = " "; 206 if (prefix != null) 207 subprefix = prefix + subprefix; 208 for (Extension e : this) 209 out.println(e.toString(subprefix)); 210 if (prefix != null) out.print(prefix); 211 out.print("};"); 212 return str.toString(); 213 } 214 215 /** 216 * List iterator interface to an extensions list. 217 * 218 * @author csm@gnu.org 219 */ 220 public final class ExtensionsIterator implements ListIterator<Extension> 221 { 222 private final int modCount; 223 private int index; 224 private final int size; 225 ExtensionsIterator()226 public ExtensionsIterator () 227 { 228 this.modCount = ExtensionList.this.modCount; 229 index = 0; 230 size = size (); 231 } 232 hasNext()233 public boolean hasNext() 234 { 235 return index < size; 236 } 237 hasPrevious()238 public boolean hasPrevious() 239 { 240 return index > 0; 241 } 242 next()243 public Extension next() throws NoSuchElementException 244 { 245 if (modCount != ExtensionList.this.modCount) 246 throw new ConcurrentModificationException (); 247 if (!hasNext ()) 248 throw new NoSuchElementException (); 249 return get (index++); 250 } 251 previous()252 public Extension previous() throws NoSuchElementException 253 { 254 if (modCount != ExtensionList.this.modCount) 255 throw new ConcurrentModificationException (); 256 if (!hasPrevious ()) 257 throw new NoSuchElementException (); 258 return get (--index); 259 } 260 nextIndex()261 public int nextIndex() 262 { 263 if (hasNext ()) 264 return index + 1; 265 return index; 266 } 267 previousIndex()268 public int previousIndex() 269 { 270 if (hasPrevious ()) 271 return index - 1; 272 return -1; 273 } 274 add(Extension e)275 public void add(Extension e) 276 { 277 throw new UnsupportedOperationException ("cannot add items to this iterator"); 278 } 279 remove()280 public void remove() 281 { 282 throw new UnsupportedOperationException ("cannot remove items from this iterator"); 283 } 284 set(Extension e)285 public void set(Extension e) 286 { 287 ExtensionList.this.set (index, e); 288 } 289 } 290 } 291