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

   1: /* ServerNameList.java --
   2:    Copyright (C) 2006  Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.net.ssl.provider;
  40: 
  41: import gnu.javax.net.ssl.provider.Extension.Value;
  42: 
  43: import java.io.PrintWriter;
  44: import java.io.StringWriter;
  45: 
  46: import java.nio.ByteBuffer;
  47: import java.nio.ByteOrder;
  48: import java.nio.CharBuffer;
  49: import java.nio.charset.CharacterCodingException;
  50: import java.nio.charset.Charset;
  51: import java.nio.charset.CharsetEncoder;
  52: import java.util.List;
  53: import java.util.NoSuchElementException;
  54: 
  55: /**
  56:  * The ServerName extension.
  57:  *
  58:  * <pre>
  59:  struct {
  60:    NameType name_type;
  61:    select (name_type) {
  62:      case host_name: HostName;
  63:    } name;
  64: } ServerName;
  65: 
  66: enum {
  67:   host_name(0), (255)
  68: } NameType;
  69: 
  70: opaque HostName<1..2^16-1>;
  71: 
  72: struct {
  73:   ServerName server_name_list<1..2^16-1>
  74: } ServerNameList;</pre>
  75:  *
  76:  * <p><b>Implementation note: this class does not currently contain a
  77:  * <code>set</code> method. If you are modifying this list, then use the
  78:  * {@link #get(int)} method, and modify the returned {@link ServerName}.
  79:  *
  80:  * @author csm
  81:  */
  82: public class ServerNameList extends Value implements Iterable<ServerNameList.ServerName>
  83: {
  84:   private ByteBuffer buffer;
  85: 
  86:   public ServerNameList (final ByteBuffer buffer)
  87:   {
  88:     this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
  89:   }
  90: 
  91:   public ServerNameList(List<ServerName> names)
  92:   {
  93:     int length = 2;
  94:     for (ServerName name : names)
  95:       length += name.length();
  96:     buffer = ByteBuffer.allocate(length);
  97:     buffer.putShort((short) (length - 2));
  98:     for (ServerName name : names)
  99:       buffer.put(name.buffer());
 100:     buffer.rewind();
 101:   }
 102: 
 103:   public int length()
 104:   {
 105:     return (buffer.getShort(0) & 0xFFFF) + 2;
 106:   }
 107: 
 108:   public ByteBuffer buffer()
 109:   {
 110:     return (ByteBuffer) buffer.duplicate().limit(length());
 111:   }
 112: 
 113:   public int size()
 114:   {
 115:     int n = 0;
 116:     final int len = length();
 117:     for (int i = 2; i < len; )
 118:       {
 119:         int l = buffer.getShort(i+1);
 120:         i += l + 3;
 121:         n++;
 122:       }
 123:     return n;
 124:   }
 125: 
 126:   public ServerName get (int index)
 127:   {
 128:     final int len = length();
 129:     if (len == 0)
 130:       throw new IndexOutOfBoundsException("0; " + index);
 131:     int n = 0;
 132:     int i;
 133:     int l = buffer.getShort(3);
 134:     for (i = 2; i < len && n < index; )
 135:       {
 136:         l = buffer.getShort(i+1);
 137:         i += l + 3;
 138:         n++;
 139:       }
 140:     if (n < index)
 141:       throw new IndexOutOfBoundsException(n + "; " + index);
 142:     ByteBuffer buf = ((ByteBuffer) buffer.duplicate().position(i).limit(i+l+3)).slice();
 143:     return new ServerName (buf);
 144:   }
 145: 
 146:   public void setLength(final int newLength)
 147:   {
 148:     if (newLength < 0 || newLength > 65535)
 149:       throw new IllegalArgumentException("length must be between 0 and 65535");
 150:     buffer.putShort(0, (short) newLength);
 151:   }
 152: 
 153:   public String toString()
 154:   {
 155:     return toString(null);
 156:   }
 157: 
 158:   public String toString(String prefix)
 159:   {
 160:     StringWriter str = new StringWriter();
 161:     PrintWriter out = new PrintWriter(str);
 162:     if (prefix != null) out.print(prefix);
 163:     out.println ("ServerNameList {");
 164:     String subprefix = "  ";
 165:     if (prefix != null)
 166:       subprefix = prefix + subprefix;
 167:     for (ServerName name : this)
 168:       {
 169:         out.println (name.toString(subprefix));
 170:       }
 171:     if (prefix != null) out.print(prefix);
 172:     out.print ("};");
 173:     return str.toString();
 174:   }
 175: 
 176:   public java.util.Iterator<ServerName> iterator()
 177:   {
 178:     return new Iterator();
 179:   }
 180: 
 181:   public class Iterator implements java.util.Iterator<ServerName>
 182:   {
 183:     private int index;
 184: 
 185:     public Iterator()
 186:     {
 187:       index = 0;
 188:     }
 189: 
 190:     public boolean hasNext()
 191:     {
 192:       return index < size();
 193:     }
 194: 
 195:     public ServerName next() throws NoSuchElementException
 196:     {
 197:       try
 198:         {
 199:           return get (index++);
 200:         }
 201:       catch (IndexOutOfBoundsException ioobe)
 202:         {
 203:           throw new NoSuchElementException();
 204:         }
 205:     }
 206: 
 207:     public void remove()
 208:     {
 209:       throw new UnsupportedOperationException();
 210:     }
 211:   }
 212: 
 213:   public static class ServerName implements Constructed
 214:   {
 215:     private ByteBuffer buffer;
 216: 
 217:     public ServerName(final ByteBuffer buffer)
 218:     {
 219:       this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
 220:     }
 221: 
 222:     public ServerName(NameType type, String name)
 223:     {
 224:       CharsetEncoder utf8 = Charset.forName("UTF-8").newEncoder();
 225:       ByteBuffer nameBuf = null;
 226:       try
 227:         {
 228:           nameBuf = utf8.encode(CharBuffer.wrap(name));
 229:         }
 230:       catch (CharacterCodingException cce)
 231:         {
 232:           // We don't expect this to happen; it's UTF-8.
 233:           throw new IllegalArgumentException(cce);
 234:         }
 235:       int length = 3 + nameBuf.remaining();
 236:       buffer = ByteBuffer.allocate(length);
 237:       buffer.put((byte) type.getValue());
 238:       buffer.putShort((short) (length - 3));
 239:       buffer.put(nameBuf);
 240:       buffer.rewind();
 241:     }
 242: 
 243:     public int length()
 244:     {
 245:       return (buffer.getShort(1) & 0xFFFF) + 3;
 246:     }
 247: 
 248:     public ByteBuffer buffer()
 249:     {
 250:       return (ByteBuffer) buffer.duplicate().limit(length());
 251:     }
 252: 
 253:     public NameType type()
 254:     {
 255:       int v = (buffer.get(0) & 0xFF);
 256:       if (v == 0)
 257:         {
 258:           return NameType.HOST_NAME;
 259:         }
 260:       throw new IllegalArgumentException ("illegal name type: " + v);
 261:     }
 262: 
 263:     public String name()
 264:     {
 265:       int len = length();
 266:       Charset cs = Charset.forName ("UTF-8");
 267:       return cs.decode(((ByteBuffer) buffer.duplicate().position(3).limit(len))).toString();
 268:     }
 269: 
 270:     public String toString()
 271:     {
 272:       return toString (null);
 273:     }
 274: 
 275:     public String toString(String prefix)
 276:     {
 277:       StringWriter str = new StringWriter();
 278:       PrintWriter out = new PrintWriter(str);
 279:       if (prefix != null) out.print (prefix);
 280:       out.println ("struct {");
 281:       if (prefix != null) out.print (prefix);
 282:       out.print ("  name_type = ");
 283:       out.print (type());
 284:       out.println (";");
 285:       if (prefix != null) out.print (prefix);
 286:       out.print ("  server_name = ");
 287:       out.print (name());
 288:       out.println (";");
 289:       if (prefix != null) out.print (prefix);
 290:       out.print ("} ServerName;");
 291:       return str.toString();
 292:     }
 293:   }
 294: 
 295:   public static enum NameType
 296:   {
 297:     HOST_NAME (0);
 298: 
 299:     private final int value;
 300: 
 301:     private NameType (int value)
 302:     {
 303:       this.value = value;
 304:     }
 305: 
 306:     public int getValue()
 307:     {
 308:       return value;
 309:     }
 310:   }
 311: }