Source for gnu.javax.net.ssl.provider.ExtensionList

   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&lt;1..2^16-1&gt;</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: 
  26:   public ExtensionList (ByteBuffer buffer)
  27:   {
  28:     this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
  29:     modCount = 0;
  30:   }
  31: 
  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: 
  44:   public ByteBuffer buffer()
  45:   {
  46:     return (ByteBuffer) buffer.duplicate().limit(length());
  47:   }
  48: 
  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:    */
  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:    */
  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:    */
 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:    */
 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:    */
 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: 
 185:   public Iterator<Extension> iterator()
 186:   {
 187:     return new ExtensionsIterator();
 188:   }
 189: 
 190:   public String toString()
 191:   {
 192:     return toString (null);
 193:   }
 194: 
 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: 
 226:     public ExtensionsIterator ()
 227:     {
 228:       this.modCount = ExtensionList.this.modCount;
 229:       index = 0;
 230:       size = size ();
 231:     }
 232: 
 233:     public boolean hasNext()
 234:     {
 235:       return index < size;
 236:     }
 237: 
 238:     public boolean hasPrevious()
 239:     {
 240:       return index > 0;
 241:     }
 242: 
 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: 
 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: 
 261:     public int nextIndex()
 262:     {
 263:       if (hasNext ())
 264:         return index + 1;
 265:       return index;
 266:     }
 267: 
 268:     public int previousIndex()
 269:     {
 270:       if (hasPrevious ())
 271:         return index - 1;
 272:       return -1;
 273:     }
 274: 
 275:     public void add(Extension e)
 276:     {
 277:       throw new UnsupportedOperationException ("cannot add items to this iterator");
 278:     }
 279: 
 280:     public void remove()
 281:     {
 282:       throw new UnsupportedOperationException ("cannot remove items from this iterator");
 283:     }
 284: 
 285:     public void set(Extension e)
 286:     {
 287:       ExtensionList.this.set (index, e);
 288:     }
 289:   }
 290: }