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

   1: /* SSLSocketImpl.java -- implementation of an SSL client socket.
   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.classpath.debug.Component;
  42: import gnu.classpath.debug.SystemLogger;
  43: 
  44: import java.io.DataInputStream;
  45: import java.io.EOFException;
  46: import java.io.IOException;
  47: import java.io.InputStream;
  48: import java.io.OutputStream;
  49: import java.net.InetAddress;
  50: import java.net.Socket;
  51: import java.net.SocketAddress;
  52: import java.net.SocketException;
  53: import java.nio.ByteBuffer;
  54: import java.nio.channels.SocketChannel;
  55: import java.util.HashSet;
  56: import java.util.Set;
  57: 
  58: import javax.net.ssl.HandshakeCompletedEvent;
  59: import javax.net.ssl.HandshakeCompletedListener;
  60: import javax.net.ssl.SSLEngineResult;
  61: import javax.net.ssl.SSLException;
  62: import javax.net.ssl.SSLSession;
  63: import javax.net.ssl.SSLSocket;
  64: import javax.net.ssl.SSLEngineResult.HandshakeStatus;
  65: import javax.net.ssl.SSLEngineResult.Status;
  66: 
  67: /**
  68:  * @author Casey Marshall (csm@gnu.org)
  69:  */
  70: public class SSLSocketImpl extends SSLSocket
  71: {
  72:   private class SocketOutputStream extends OutputStream
  73:   {
  74:     private final ByteBuffer buffer;
  75:     private final OutputStream out;
  76: 
  77:     SocketOutputStream() throws IOException
  78:     {
  79:       buffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
  80:       if (underlyingSocket != null)
  81:         out = underlyingSocket.getOutputStream();
  82:       else
  83:         out = SSLSocketImpl.super.getOutputStream();
  84:     }
  85: 
  86:     @Override public void write(byte[] buf, int off, int len) throws IOException
  87:     {
  88:       if (!initialHandshakeDone
  89:           || engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
  90:         {
  91:           doHandshake();
  92:           if (handshakeException != null)
  93:             throw handshakeException;
  94:         }
  95: 
  96:       int k = 0;
  97:       while (k < len)
  98:         {
  99:           synchronized (engine)
 100:             {
 101:               int l = Math.min(len-k, getSession().getApplicationBufferSize());
 102:               ByteBuffer in = ByteBuffer.wrap(buf, off+k, l);
 103:               SSLEngineResult result = engine.wrap(in, buffer);
 104:               if (result.getStatus() == Status.CLOSED)
 105:                 return;
 106:               if (result.getStatus() != Status.OK)
 107:                 throw new SSLException("unexpected SSL state " + result.getStatus());
 108:               buffer.flip();
 109:               out.write(buffer.array(), 0, buffer.limit());
 110:               k += result.bytesConsumed();
 111:               buffer.clear();
 112:             }
 113:         }
 114:     }
 115: 
 116:     @Override public void write(int b) throws IOException
 117:     {
 118:       write(new byte[] { (byte) b });
 119:     }
 120: 
 121:     @Override public void close() throws IOException
 122:     {
 123:       SSLSocketImpl.this.close();
 124:     }
 125:   }
 126: 
 127:   private class SocketInputStream extends InputStream
 128:   {
 129:     private final ByteBuffer inBuffer;
 130:     private final ByteBuffer appBuffer;
 131:     private final DataInputStream in;
 132: 
 133:     SocketInputStream() throws IOException
 134:     {
 135:       inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
 136:       inBuffer.limit(0);
 137:       appBuffer = ByteBuffer.allocate(getSession().getApplicationBufferSize());
 138:       appBuffer.flip();
 139:       if (underlyingSocket != null)
 140:         in = new DataInputStream(underlyingSocket.getInputStream());
 141:       else
 142:         in = new DataInputStream(SSLSocketImpl.super.getInputStream());
 143:     }
 144: 
 145:     @Override public int read(byte[] buf, int off, int len) throws IOException
 146:     {
 147:       if (!initialHandshakeDone ||
 148:           engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
 149:         {
 150:           doHandshake();
 151:           if (handshakeException != null)
 152:             throw handshakeException;
 153:         }
 154: 
 155:       if (!appBuffer.hasRemaining())
 156:         {
 157:           int x = in.read();
 158:           if (x == -1)
 159:             return -1;
 160:           inBuffer.clear();
 161:           inBuffer.put((byte) x);
 162:           inBuffer.putInt(in.readInt());
 163:           int reclen = inBuffer.getShort(3) & 0xFFFF;
 164:           in.readFully(inBuffer.array(), 5, reclen);
 165:           inBuffer.position(0).limit(reclen + 5);
 166:           synchronized (engine)
 167:             {
 168:               appBuffer.clear();
 169:               SSLEngineResult result = engine.unwrap(inBuffer, appBuffer);
 170:               Status status = result.getStatus();
 171:               if (status == Status.CLOSED && result.bytesProduced() == 0)
 172:                 return -1;
 173:             }
 174:           inBuffer.compact();
 175:           appBuffer.flip();
 176:         }
 177:       int l = Math.min(len, appBuffer.remaining());
 178:       appBuffer.get(buf, off, l);
 179:       return l;
 180:     }
 181: 
 182:     @Override public int read() throws IOException
 183:     {
 184:       byte[] b = new byte[1];
 185:       if (read(b) == -1)
 186:         return -1;
 187:       return b[0] & 0xFF;
 188:     }
 189:   }
 190: 
 191:   private static final SystemLogger logger = SystemLogger.getSystemLogger();
 192: 
 193:   private SSLEngineImpl engine;
 194:   private Set<HandshakeCompletedListener> listeners;
 195:   private Socket underlyingSocket;
 196:   private boolean isHandshaking;
 197:   private IOException handshakeException;
 198:   private boolean initialHandshakeDone = false;
 199:   private final boolean autoClose;
 200: 
 201:   public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)
 202:   {
 203:     this(contextImpl, host, port, new Socket(), true);
 204:   }
 205: 
 206:   public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port,
 207:                        Socket underlyingSocket, boolean autoClose)
 208:   {
 209:     engine = new SSLEngineImpl(contextImpl, host, port);
 210:     engine.setUseClientMode(true); // default to client mode
 211:     listeners = new HashSet<HandshakeCompletedListener>();
 212:     this.underlyingSocket = underlyingSocket;
 213:     this.autoClose = autoClose;
 214:   }
 215: 
 216:   /* (non-Javadoc)
 217:    * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
 218:    */
 219:   @Override
 220:   public void addHandshakeCompletedListener(HandshakeCompletedListener listener)
 221:   {
 222:     listeners.add(listener);
 223:   }
 224: 
 225:   /* (non-Javadoc)
 226:    * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
 227:    */
 228:   @Override public boolean getEnableSessionCreation()
 229:   {
 230:     return engine.getEnableSessionCreation();
 231:   }
 232: 
 233:   /* (non-Javadoc)
 234:    * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
 235:    */
 236:   @Override public String[] getEnabledCipherSuites()
 237:   {
 238:     return engine.getEnabledCipherSuites();
 239:   }
 240: 
 241:   /* (non-Javadoc)
 242:    * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
 243:    */
 244:   @Override public String[] getEnabledProtocols()
 245:   {
 246:     return engine.getEnabledProtocols();
 247:   }
 248: 
 249:   /* (non-Javadoc)
 250:    * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
 251:    */
 252:   @Override public boolean getNeedClientAuth()
 253:   {
 254:     return engine.getNeedClientAuth();
 255:   }
 256: 
 257:   /* (non-Javadoc)
 258:    * @see javax.net.ssl.SSLSocket#getSession()
 259:    */
 260:   @Override public SSLSession getSession()
 261:   {
 262:     return engine.getSession();
 263:   }
 264: 
 265:   /* (non-Javadoc)
 266:    * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
 267:    */
 268:   @Override public String[] getSupportedCipherSuites()
 269:   {
 270:     return engine.getSupportedCipherSuites();
 271:   }
 272: 
 273:   /* (non-Javadoc)
 274:    * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
 275:    */
 276:   @Override public String[] getSupportedProtocols()
 277:   {
 278:     return engine.getSupportedProtocols();
 279:   }
 280: 
 281:   /* (non-Javadoc)
 282:    * @see javax.net.ssl.SSLSocket#getUseClientMode()
 283:    */
 284:   @Override public boolean getUseClientMode()
 285:   {
 286:     return engine.getUseClientMode();
 287:   }
 288: 
 289:   /* (non-Javadoc)
 290:    * @see javax.net.ssl.SSLSocket#getWantClientAuth()
 291:    */
 292:   @Override public boolean getWantClientAuth()
 293:   {
 294:     return engine.getWantClientAuth();
 295:   }
 296: 
 297:   /* (non-Javadoc)
 298:    * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
 299:    */
 300:   @Override
 301:   public void removeHandshakeCompletedListener(HandshakeCompletedListener listener)
 302:   {
 303:     listeners.remove(listener);
 304:   }
 305: 
 306:   /* (non-Javadoc)
 307:    * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean)
 308:    */
 309:   @Override public void setEnableSessionCreation(boolean enable)
 310:   {
 311:     engine.setEnableSessionCreation(enable);
 312:   }
 313: 
 314:   /* (non-Javadoc)
 315:    * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(java.lang.String[])
 316:    */
 317:   @Override public void setEnabledCipherSuites(String[] suites)
 318:   {
 319:     engine.setEnabledCipherSuites(suites);
 320:   }
 321: 
 322:   /* (non-Javadoc)
 323:    * @see javax.net.ssl.SSLSocket#setEnabledProtocols(java.lang.String[])
 324:    */
 325:   @Override public void setEnabledProtocols(String[] protocols)
 326:   {
 327:     engine.setEnabledProtocols(protocols);
 328:   }
 329: 
 330:   /* (non-Javadoc)
 331:    * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean)
 332:    */
 333:   @Override public void setNeedClientAuth(boolean needAuth)
 334:   {
 335:     engine.setNeedClientAuth(needAuth);
 336:   }
 337: 
 338:   /* (non-Javadoc)
 339:    * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean)
 340:    */
 341:   @Override public void setUseClientMode(boolean clientMode)
 342:   {
 343:     engine.setUseClientMode(clientMode);
 344:   }
 345: 
 346:   /* (non-Javadoc)
 347:    * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean)
 348:    */
 349:   @Override public void setWantClientAuth(boolean wantAuth)
 350:   {
 351:     engine.setWantClientAuth(wantAuth);
 352:   }
 353: 
 354:   /* (non-Javadoc)
 355:    * @see javax.net.ssl.SSLSocket#startHandshake()
 356:    */
 357:   @Override public void startHandshake() throws IOException
 358:   {
 359:     if (isHandshaking)
 360:       return;
 361: 
 362:     if (handshakeException != null)
 363:       throw handshakeException;
 364: 
 365:     Thread t = new Thread(new Runnable()
 366:     {
 367:       public void run()
 368:       {
 369:         try
 370:           {
 371:             doHandshake();
 372:           }
 373:         catch (IOException ioe)
 374:           {
 375:             handshakeException = ioe;
 376:           }
 377:       }
 378:     }, "HandshakeThread@" + System.identityHashCode(this));
 379:     t.start();
 380:   }
 381: 
 382:   void doHandshake() throws IOException
 383:   {
 384:     synchronized (engine)
 385:       {
 386:         if (isHandshaking)
 387:           {
 388:             try
 389:               {
 390:                 engine.wait();
 391:               }
 392:             catch (InterruptedException ie)
 393:               {
 394:               }
 395:             return;
 396:           }
 397:         isHandshaking = true;
 398:       }
 399: 
 400:     if (initialHandshakeDone)
 401:       throw new SSLException("rehandshaking not yet implemented");
 402: 
 403:     long now = -System.currentTimeMillis();
 404:     engine.beginHandshake();
 405: 
 406:     HandshakeStatus status = engine.getHandshakeStatus();
 407:     assert(status != HandshakeStatus.NOT_HANDSHAKING);
 408: 
 409:     ByteBuffer inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
 410:     inBuffer.position(inBuffer.limit());
 411:     ByteBuffer outBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
 412:     ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
 413:     SSLEngineResult result = null;
 414: 
 415:     DataInputStream sockIn = new DataInputStream(underlyingSocket.getInputStream());
 416:     OutputStream sockOut = underlyingSocket.getOutputStream();
 417: 
 418:     try
 419:       {
 420:         while (status != HandshakeStatus.NOT_HANDSHAKING
 421:                && status != HandshakeStatus.FINISHED)
 422:           {
 423:             logger.logv(Component.SSL_HANDSHAKE, "socket processing state {0}",
 424:                         status);
 425: 
 426:             if (inBuffer.capacity() != getSession().getPacketBufferSize())
 427:               {
 428:                 ByteBuffer b
 429:                   = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
 430:                 if (inBuffer.hasRemaining())
 431:                   b.put(inBuffer).flip();
 432:                 inBuffer = b;
 433:               }
 434:             if (outBuffer.capacity() != getSession().getPacketBufferSize())
 435:               outBuffer
 436:               = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
 437: 
 438:             switch (status)
 439:               {
 440:                 case NEED_UNWRAP:
 441:                   // Read in a single SSL record.
 442:                   inBuffer.clear();
 443:                   int i = sockIn.read();
 444:                   if (i == -1)
 445:                     throw new EOFException();
 446:                   if ((i & 0x80) == 0x80) // SSLv2 client hello.
 447:                     {
 448:                       inBuffer.put((byte) i);
 449:                       int v2len = (i & 0x7f) << 8;
 450:                       i = sockIn.read();
 451:                       v2len = v2len | (i & 0xff);
 452:                       inBuffer.put((byte) i);
 453:                       sockIn.readFully(inBuffer.array(), 2, v2len);
 454:                       inBuffer.position(0).limit(v2len + 2);
 455:                     }
 456:                   else
 457:                     {
 458:                       inBuffer.put((byte) i);
 459:                       inBuffer.putInt(sockIn.readInt());
 460:                       int reclen = inBuffer.getShort(3) & 0xFFFF;
 461:                       sockIn.readFully(inBuffer.array(), 5, reclen);
 462:                       inBuffer.position(0).limit(reclen + 5);
 463:                     }
 464:                   result = engine.unwrap(inBuffer, emptyBuffer);
 465:                   status = result.getHandshakeStatus();
 466:                   if (result.getStatus() != Status.OK)
 467:                     throw new SSLException("unexpected SSL status "
 468:                                            + result.getStatus());
 469:                   break;
 470: 
 471:                 case NEED_WRAP:
 472:                 {
 473:                   outBuffer.clear();
 474:                   result = engine.wrap(emptyBuffer, outBuffer);
 475:                   status = result.getHandshakeStatus();
 476:                   if (result.getStatus() != Status.OK)
 477:                     throw new SSLException("unexpected SSL status "
 478:                                            + result.getStatus());
 479:                   outBuffer.flip();
 480:                   sockOut.write(outBuffer.array(), outBuffer.position(),
 481:                                 outBuffer.limit());
 482:                 }
 483:                 break;
 484: 
 485:                 case NEED_TASK:
 486:                 {
 487:                   Runnable task;
 488:                   while ((task = engine.getDelegatedTask()) != null)
 489:                     task.run();
 490:                   status = engine.getHandshakeStatus();
 491:                 }
 492:                 break;
 493: 
 494:                 case FINISHED:
 495:                   break;
 496:               }
 497:           }
 498: 
 499:         initialHandshakeDone = true;
 500: 
 501:         HandshakeCompletedEvent hce = new HandshakeCompletedEvent(this, getSession());
 502:         for (HandshakeCompletedListener l : listeners)
 503:           {
 504:             try
 505:               {
 506:                 l.handshakeCompleted(hce);
 507:               }
 508:             catch (ThreadDeath td)
 509:               {
 510:                 throw td;
 511:               }
 512:             catch (Throwable x)
 513:               {
 514:                 logger.log(Component.WARNING,
 515:                            "HandshakeCompletedListener threw exception", x);
 516:               }
 517:           }
 518: 
 519:         now += System.currentTimeMillis();
 520:         if (Debug.DEBUG)
 521:           logger.logv(Component.SSL_HANDSHAKE,
 522:                       "handshake completed in {0}ms in thread {1}", now,
 523:                       Thread.currentThread().getName());
 524:       }
 525:     catch (SSLException ssle)
 526:       {
 527:         handshakeException = ssle;
 528:         throw ssle;
 529:       }
 530:     finally
 531:       {
 532:         synchronized (engine)
 533:           {
 534:             isHandshaking = false;
 535:             engine.notifyAll();
 536:           }
 537:       }
 538:   }
 539: 
 540:   // Methods overriding Socket.
 541: 
 542:   @Override public void bind(SocketAddress bindpoint) throws IOException
 543:   {
 544:     underlyingSocket.bind(bindpoint);
 545:   }
 546: 
 547:   @Override public void connect(SocketAddress endpoint) throws IOException
 548:   {
 549:     underlyingSocket.connect(endpoint);
 550:   }
 551: 
 552:   @Override public void connect(SocketAddress endpoint, int timeout)
 553:     throws IOException
 554:   {
 555:     underlyingSocket.connect(endpoint, timeout);
 556:   }
 557: 
 558:   @Override public InetAddress getInetAddress()
 559:   {
 560:     return underlyingSocket.getInetAddress();
 561:   }
 562: 
 563:   @Override public InetAddress getLocalAddress()
 564:   {
 565:     return underlyingSocket.getLocalAddress();
 566:   }
 567: 
 568:   @Override public int getPort()
 569:   {
 570:     return underlyingSocket.getPort();
 571:   }
 572: 
 573:   @Override public int getLocalPort()
 574:   {
 575:     return underlyingSocket.getLocalPort();
 576:   }
 577: 
 578:   @Override public SocketAddress getRemoteSocketAddress()
 579:   {
 580:     return underlyingSocket.getRemoteSocketAddress();
 581:   }
 582: 
 583:   public SocketAddress getLocalSocketAddress()
 584:   {
 585:     return underlyingSocket.getLocalSocketAddress();
 586:   }
 587: 
 588:   @Override public SocketChannel getChannel()
 589:   {
 590:     throw new UnsupportedOperationException("use javax.net.ssl.SSLEngine for NIO");
 591:   }
 592: 
 593:   @Override public InputStream getInputStream() throws IOException
 594:   {
 595:     return new SocketInputStream();
 596:   }
 597: 
 598:   @Override public OutputStream getOutputStream() throws IOException
 599:   {
 600:     return new SocketOutputStream();
 601:   }
 602: 
 603:   @Override public void setTcpNoDelay(boolean on) throws SocketException
 604:   {
 605:     underlyingSocket.setTcpNoDelay(on);
 606:   }
 607: 
 608:   @Override public boolean getTcpNoDelay() throws SocketException
 609:   {
 610:     return underlyingSocket.getTcpNoDelay();
 611:   }
 612: 
 613:   @Override public void setSoLinger(boolean on, int linger) throws SocketException
 614:   {
 615:     underlyingSocket.setSoLinger(on, linger);
 616:   }
 617: 
 618:   public int getSoLinger() throws SocketException
 619:   {
 620:     return underlyingSocket.getSoLinger();
 621:   }
 622: 
 623:   @Override public void sendUrgentData(int x) throws IOException
 624:   {
 625:     throw new UnsupportedOperationException("not supported");
 626:   }
 627: 
 628:   @Override public void setOOBInline(boolean on) throws SocketException
 629:   {
 630:     underlyingSocket.setOOBInline(on);
 631:   }
 632: 
 633:   @Override public boolean getOOBInline() throws SocketException
 634:   {
 635:     return underlyingSocket.getOOBInline();
 636:   }
 637: 
 638:   @Override public void setSoTimeout(int timeout) throws SocketException
 639:   {
 640:     underlyingSocket.setSoTimeout(timeout);
 641:   }
 642: 
 643:   @Override public int getSoTimeout() throws SocketException
 644:   {
 645:     return underlyingSocket.getSoTimeout();
 646:   }
 647: 
 648:   @Override public void setSendBufferSize(int size) throws SocketException
 649:   {
 650:     underlyingSocket.setSendBufferSize(size);
 651:   }
 652: 
 653:   @Override public int getSendBufferSize() throws SocketException
 654:   {
 655:     return underlyingSocket.getSendBufferSize();
 656:   }
 657: 
 658:   @Override public void setReceiveBufferSize(int size) throws SocketException
 659:   {
 660:     underlyingSocket.setReceiveBufferSize(size);
 661:   }
 662: 
 663:   @Override public int getReceiveBufferSize() throws SocketException
 664:   {
 665:     return underlyingSocket.getReceiveBufferSize();
 666:   }
 667: 
 668:   @Override public void setKeepAlive(boolean on) throws SocketException
 669:   {
 670:     underlyingSocket.setKeepAlive(on);
 671:   }
 672: 
 673:   @Override public boolean getKeepAlive() throws SocketException
 674:   {
 675:     return underlyingSocket.getKeepAlive();
 676:   }
 677: 
 678:   @Override public void setTrafficClass(int tc) throws SocketException
 679:   {
 680:     underlyingSocket.setTrafficClass(tc);
 681:   }
 682: 
 683:   @Override public int getTrafficClass() throws SocketException
 684:   {
 685:     return underlyingSocket.getTrafficClass();
 686:   }
 687: 
 688:   @Override public void setReuseAddress(boolean reuseAddress)
 689:     throws SocketException
 690:   {
 691:     underlyingSocket.setReuseAddress(reuseAddress);
 692:   }
 693: 
 694:   @Override public boolean getReuseAddress() throws SocketException
 695:   {
 696:     return underlyingSocket.getReuseAddress();
 697:   }
 698: 
 699:   @Override public void close() throws IOException
 700:   {
 701:     // XXX closure alerts.
 702:     if (autoClose)
 703:       underlyingSocket.close();
 704:   }
 705: 
 706:   @Override public void shutdownInput() throws IOException
 707:   {
 708:     underlyingSocket.shutdownInput();
 709:   }
 710: 
 711:   @Override public void shutdownOutput() throws IOException
 712:   {
 713:     underlyingSocket.shutdownOutput();
 714:   }
 715: 
 716:   @Override public boolean isConnected()
 717:   {
 718:     return underlyingSocket.isConnected();
 719:   }
 720: 
 721:   @Override public boolean isBound()
 722:   {
 723:     return underlyingSocket.isBound();
 724:   }
 725: 
 726:   @Override public boolean isClosed()
 727:   {
 728:     return underlyingSocket.isClosed();
 729:   }
 730: 
 731:   @Override public boolean isInputShutdown()
 732:   {
 733:     return underlyingSocket.isInputShutdown();
 734:   }
 735: 
 736:   @Override public boolean isOutputShutdown()
 737:   {
 738:     return underlyingSocket.isOutputShutdown();
 739:   }
 740: }