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 
SocketOutputStream()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 
write(byte[] buf, int off, int len)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 
write(int b)116     @Override public void write(int b) throws IOException
117     {
118       write(new byte[] { (byte) b });
119     }
120 
close()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 
SocketInputStream()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 
read(byte[] buf, int off, int len)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 
read()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 
SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)201   public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)
202   {
203     this(contextImpl, host, port, new Socket(), true);
204   }
205 
SSLSocketImpl(SSLContextImpl contextImpl, String host, int port, Socket underlyingSocket, boolean autoClose)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
addHandshakeCompletedListener(HandshakeCompletedListener listener)220   public void addHandshakeCompletedListener(HandshakeCompletedListener listener)
221   {
222     listeners.add(listener);
223   }
224 
225   /* (non-Javadoc)
226    * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
227    */
getEnableSessionCreation()228   @Override public boolean getEnableSessionCreation()
229   {
230     return engine.getEnableSessionCreation();
231   }
232 
233   /* (non-Javadoc)
234    * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
235    */
getEnabledCipherSuites()236   @Override public String[] getEnabledCipherSuites()
237   {
238     return engine.getEnabledCipherSuites();
239   }
240 
241   /* (non-Javadoc)
242    * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
243    */
getEnabledProtocols()244   @Override public String[] getEnabledProtocols()
245   {
246     return engine.getEnabledProtocols();
247   }
248 
249   /* (non-Javadoc)
250    * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
251    */
getNeedClientAuth()252   @Override public boolean getNeedClientAuth()
253   {
254     return engine.getNeedClientAuth();
255   }
256 
257   /* (non-Javadoc)
258    * @see javax.net.ssl.SSLSocket#getSession()
259    */
getSession()260   @Override public SSLSession getSession()
261   {
262     return engine.getSession();
263   }
264 
265   /* (non-Javadoc)
266    * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
267    */
getSupportedCipherSuites()268   @Override public String[] getSupportedCipherSuites()
269   {
270     return engine.getSupportedCipherSuites();
271   }
272 
273   /* (non-Javadoc)
274    * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
275    */
getSupportedProtocols()276   @Override public String[] getSupportedProtocols()
277   {
278     return engine.getSupportedProtocols();
279   }
280 
281   /* (non-Javadoc)
282    * @see javax.net.ssl.SSLSocket#getUseClientMode()
283    */
getUseClientMode()284   @Override public boolean getUseClientMode()
285   {
286     return engine.getUseClientMode();
287   }
288 
289   /* (non-Javadoc)
290    * @see javax.net.ssl.SSLSocket#getWantClientAuth()
291    */
getWantClientAuth()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
removeHandshakeCompletedListener(HandshakeCompletedListener listener)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    */
setEnableSessionCreation(boolean enable)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    */
setEnabledCipherSuites(String[] suites)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    */
setEnabledProtocols(String[] protocols)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    */
setNeedClientAuth(boolean needAuth)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    */
setUseClientMode(boolean clientMode)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    */
setWantClientAuth(boolean wantAuth)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    */
startHandshake()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 
doHandshake()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 
bind(SocketAddress bindpoint)542   @Override public void bind(SocketAddress bindpoint) throws IOException
543   {
544     underlyingSocket.bind(bindpoint);
545   }
546 
connect(SocketAddress endpoint)547   @Override public void connect(SocketAddress endpoint) throws IOException
548   {
549     underlyingSocket.connect(endpoint);
550   }
551 
connect(SocketAddress endpoint, int timeout)552   @Override public void connect(SocketAddress endpoint, int timeout)
553     throws IOException
554   {
555     underlyingSocket.connect(endpoint, timeout);
556   }
557 
getInetAddress()558   @Override public InetAddress getInetAddress()
559   {
560     return underlyingSocket.getInetAddress();
561   }
562 
getLocalAddress()563   @Override public InetAddress getLocalAddress()
564   {
565     return underlyingSocket.getLocalAddress();
566   }
567 
getPort()568   @Override public int getPort()
569   {
570     return underlyingSocket.getPort();
571   }
572 
getLocalPort()573   @Override public int getLocalPort()
574   {
575     return underlyingSocket.getLocalPort();
576   }
577 
getRemoteSocketAddress()578   @Override public SocketAddress getRemoteSocketAddress()
579   {
580     return underlyingSocket.getRemoteSocketAddress();
581   }
582 
getLocalSocketAddress()583   public SocketAddress getLocalSocketAddress()
584   {
585     return underlyingSocket.getLocalSocketAddress();
586   }
587 
getChannel()588   @Override public SocketChannel getChannel()
589   {
590     throw new UnsupportedOperationException("use javax.net.ssl.SSLEngine for NIO");
591   }
592 
getInputStream()593   @Override public InputStream getInputStream() throws IOException
594   {
595     return new SocketInputStream();
596   }
597 
getOutputStream()598   @Override public OutputStream getOutputStream() throws IOException
599   {
600     return new SocketOutputStream();
601   }
602 
setTcpNoDelay(boolean on)603   @Override public void setTcpNoDelay(boolean on) throws SocketException
604   {
605     underlyingSocket.setTcpNoDelay(on);
606   }
607 
getTcpNoDelay()608   @Override public boolean getTcpNoDelay() throws SocketException
609   {
610     return underlyingSocket.getTcpNoDelay();
611   }
612 
setSoLinger(boolean on, int linger)613   @Override public void setSoLinger(boolean on, int linger) throws SocketException
614   {
615     underlyingSocket.setSoLinger(on, linger);
616   }
617 
getSoLinger()618   public int getSoLinger() throws SocketException
619   {
620     return underlyingSocket.getSoLinger();
621   }
622 
sendUrgentData(int x)623   @Override public void sendUrgentData(int x) throws IOException
624   {
625     throw new UnsupportedOperationException("not supported");
626   }
627 
setOOBInline(boolean on)628   @Override public void setOOBInline(boolean on) throws SocketException
629   {
630     underlyingSocket.setOOBInline(on);
631   }
632 
getOOBInline()633   @Override public boolean getOOBInline() throws SocketException
634   {
635     return underlyingSocket.getOOBInline();
636   }
637 
setSoTimeout(int timeout)638   @Override public void setSoTimeout(int timeout) throws SocketException
639   {
640     underlyingSocket.setSoTimeout(timeout);
641   }
642 
getSoTimeout()643   @Override public int getSoTimeout() throws SocketException
644   {
645     return underlyingSocket.getSoTimeout();
646   }
647 
setSendBufferSize(int size)648   @Override public void setSendBufferSize(int size) throws SocketException
649   {
650     underlyingSocket.setSendBufferSize(size);
651   }
652 
getSendBufferSize()653   @Override public int getSendBufferSize() throws SocketException
654   {
655     return underlyingSocket.getSendBufferSize();
656   }
657 
setReceiveBufferSize(int size)658   @Override public void setReceiveBufferSize(int size) throws SocketException
659   {
660     underlyingSocket.setReceiveBufferSize(size);
661   }
662 
getReceiveBufferSize()663   @Override public int getReceiveBufferSize() throws SocketException
664   {
665     return underlyingSocket.getReceiveBufferSize();
666   }
667 
setKeepAlive(boolean on)668   @Override public void setKeepAlive(boolean on) throws SocketException
669   {
670     underlyingSocket.setKeepAlive(on);
671   }
672 
getKeepAlive()673   @Override public boolean getKeepAlive() throws SocketException
674   {
675     return underlyingSocket.getKeepAlive();
676   }
677 
setTrafficClass(int tc)678   @Override public void setTrafficClass(int tc) throws SocketException
679   {
680     underlyingSocket.setTrafficClass(tc);
681   }
682 
getTrafficClass()683   @Override public int getTrafficClass() throws SocketException
684   {
685     return underlyingSocket.getTrafficClass();
686   }
687 
setReuseAddress(boolean reuseAddress)688   @Override public void setReuseAddress(boolean reuseAddress)
689     throws SocketException
690   {
691     underlyingSocket.setReuseAddress(reuseAddress);
692   }
693 
getReuseAddress()694   @Override public boolean getReuseAddress() throws SocketException
695   {
696     return underlyingSocket.getReuseAddress();
697   }
698 
close()699   @Override public void close() throws IOException
700   {
701     // XXX closure alerts.
702     if (autoClose)
703       underlyingSocket.close();
704   }
705 
shutdownInput()706   @Override public void shutdownInput() throws IOException
707   {
708     underlyingSocket.shutdownInput();
709   }
710 
shutdownOutput()711   @Override public void shutdownOutput() throws IOException
712   {
713     underlyingSocket.shutdownOutput();
714   }
715 
isConnected()716   @Override public boolean isConnected()
717   {
718     return underlyingSocket.isConnected();
719   }
720 
isBound()721   @Override public boolean isBound()
722   {
723     return underlyingSocket.isBound();
724   }
725 
isClosed()726   @Override public boolean isClosed()
727   {
728     return underlyingSocket.isClosed();
729   }
730 
isInputShutdown()731   @Override public boolean isInputShutdown()
732   {
733     return underlyingSocket.isInputShutdown();
734   }
735 
isOutputShutdown()736   @Override public boolean isOutputShutdown()
737   {
738     return underlyingSocket.isOutputShutdown();
739   }
740 }
741