History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/httpserver/SSLStreams.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/httpserver/SSLStreams.java+662−0
@@ -0,0 +1,662 @@
1+/*
2+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.net.httpserver;
27+
28+import java.net.*;
29+import java.nio.*;
30+import java.io.*;
31+import java.nio.channels.*;
32+import java.util.*;
33+import java.util.concurrent.*;
34+import java.util.concurrent.locks.*;
35+import javax.net.ssl.*;
36+import javax.net.ssl.SSLEngineResult.*;
37+import com.sun.net.httpserver.*;
38+import com.sun.net.httpserver.spi.*;
39+
40+/**
41+ * given a non-blocking SocketChannel, it produces
42+ * (blocking) streams which encrypt/decrypt the SSL content
43+ * and handle the SSL handshaking automatically.
44+ */
45+
46+class SSLStreams {
47+
48+ SSLContext sslctx;
49+ SocketChannel chan;
50+ TimeSource time;
51+ ServerImpl server;
52+ SSLEngine engine;
53+ EngineWrapper wrapper;
54+ OutputStream os;
55+ InputStream is;
56+
57+ /* held by thread doing the hand-shake on this connection */
58+ Lock handshaking = new ReentrantLock();
59+
60+ SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException {
61+ this.server = server;
62+ this.time= (TimeSource)server;
63+ this.sslctx= sslctx;
64+ this.chan= chan;
65+ InetSocketAddress addr =
66+ (InetSocketAddress)chan.socket().getRemoteSocketAddress();
67+ engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort());
68+ engine.setUseClientMode (false);
69+ HttpsConfigurator cfg = server.getHttpsConfigurator();
70+ configureEngine (cfg, addr);
71+ wrapper = new EngineWrapper (chan, engine);
72+ }
73+
74+ private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){
75+ if (cfg != null) {
76+ Parameters params = new Parameters (cfg, addr);
77+//BEGIN_TIGER_EXCLUDE
78+ cfg.configure (params);
79+ SSLParameters sslParams = params.getSSLParameters();
80+ if (sslParams != null) {
81+ engine.setSSLParameters (sslParams);
82+ } else
83+//END_TIGER_EXCLUDE
84+ {
85+ /* tiger compatibility */
86+ if (params.getCipherSuites() != null) {
87+ try {
88+ engine.setEnabledCipherSuites (
89+ params.getCipherSuites()
90+ );
91+ } catch (IllegalArgumentException e) { /* LOG */}
92+ }
93+ engine.setNeedClientAuth (params.getNeedClientAuth());
94+ engine.setWantClientAuth (params.getWantClientAuth());
95+ if (params.getProtocols() != null) {
96+ try {
97+ engine.setEnabledProtocols (
98+ params.getProtocols()
99+ );
100+ } catch (IllegalArgumentException e) { /* LOG */}
101+ }
102+ }
103+ }
104+ }
105+
106+ class Parameters extends HttpsParameters {
107+ InetSocketAddress addr;
108+ HttpsConfigurator cfg;
109+
110+ Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {
111+ this.addr = addr;
112+ this.cfg = cfg;
113+ }
114+ public InetSocketAddress getClientAddress () {
115+ return addr;
116+ }
117+ public HttpsConfigurator getHttpsConfigurator() {
118+ return cfg;
119+ }
120+//BEGIN_TIGER_EXCLUDE
121+ SSLParameters params;
122+ public void setSSLParameters (SSLParameters p) {
123+ params = p;
124+ }
125+ SSLParameters getSSLParameters () {
126+ return params;
127+ }
128+//END_TIGER_EXCLUDE
129+ }
130+
131+ /**
132+ * cleanup resources allocated inside this object
133+ */
134+ void close () throws IOException {
135+ wrapper.close();
136+ }
137+
138+ /**
139+ * return the SSL InputStream
140+ */
141+ InputStream getInputStream () throws IOException {
142+ if (is == null) {
143+ is = new InputStream();
144+ }
145+ return is;
146+ }
147+
148+ /**
149+ * return the SSL OutputStream
150+ */
151+ OutputStream getOutputStream () throws IOException {
152+ if (os == null) {
153+ os = new OutputStream();
154+ }
155+ return os;
156+ }
157+
158+ SSLEngine getSSLEngine () {
159+ return engine;
160+ }
161+
162+ /**
163+ * request the engine to repeat the handshake on this session
164+ * the handshake must be driven by reads/writes on the streams
165+ * Normally, not necessary to call this.
166+ */
167+ void beginHandshake() throws SSLException {
168+ engine.beginHandshake();
169+ }
170+
171+ class WrapperResult {
172+ SSLEngineResult result;
173+
174+ /* if passed in buffer was not big enough then the
175+ * a reallocated buffer is returned here
176+ */
177+ ByteBuffer buf;
178+ }
179+
180+ int app_buf_size;
181+ int packet_buf_size;
182+
183+ enum BufType {
184+ PACKET, APPLICATION
185+ };
186+
187+ private ByteBuffer allocate (BufType type) {
188+ return allocate (type, -1);
189+ }
190+
191+ private ByteBuffer allocate (BufType type, int len) {
192+ assert engine != null;
193+ synchronized (this) {
194+ int size;
195+ if (type == BufType.PACKET) {
196+ if (packet_buf_size == 0) {
197+ SSLSession sess = engine.getSession();
198+ packet_buf_size = sess.getPacketBufferSize();
199+ }
200+ if (len > packet_buf_size) {
201+ packet_buf_size = len;
202+ }
203+ size = packet_buf_size;
204+ } else {
205+ if (app_buf_size == 0) {
206+ SSLSession sess = engine.getSession();
207+ app_buf_size = sess.getApplicationBufferSize();
208+ }
209+ if (len > app_buf_size) {
210+ app_buf_size = len;
211+ }
212+ size = app_buf_size;
213+ }
214+ return ByteBuffer.allocate (size);
215+ }
216+ }
217+
218+ /* reallocates the buffer by :-
219+ * 1. creating a new buffer double the size of the old one
220+ * 2. putting the contents of the old buffer into the new one
221+ * 3. set xx_buf_size to the new size if it was smaller than new size
222+ *
223+ * flip is set to true if the old buffer needs to be flipped
224+ * before it is copied.
225+ */
226+ private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
227+ synchronized (this) {
228+ int nsize = 2 * b.capacity();
229+ ByteBuffer n = allocate (type, nsize);
230+ if (flip) {
231+ b.flip();
232+ }
233+ n.put(b);
234+ b = n;
235+ }
236+ return b;
237+ }
238+ /**
239+ * This is a thin wrapper over SSLEngine and the SocketChannel,
240+ * which guarantees the ordering of wraps/unwraps with respect to the underlying
241+ * channel read/writes. It handles the UNDER/OVERFLOW status codes
242+ * It does not handle the handshaking status codes, or the CLOSED status code
243+ * though once the engine is closed, any attempt to read/write to it
244+ * will get an exception. The overall result is returned.
245+ * It functions synchronously/blocking
246+ */
247+ class EngineWrapper {
248+
249+ SocketChannel chan;
250+ SSLEngine engine;
251+ Object wrapLock, unwrapLock;
252+ ByteBuffer unwrap_src, wrap_dst;
253+ boolean closed = false;
254+ int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
255+
256+ EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {
257+ this.chan = chan;
258+ this.engine = engine;
259+ wrapLock = new Object();
260+ unwrapLock = new Object();
261+ unwrap_src = allocate(BufType.PACKET);
262+ wrap_dst = allocate(BufType.PACKET);
263+ }
264+
265+ void close () throws IOException {
266+ }
267+
268+ /* try to wrap and send the data in src. Handles OVERFLOW.
269+ * Might block if there is an outbound blockage or if another
270+ * thread is calling wrap(). Also, might not send any data
271+ * if an unwrap is needed.
272+ */
273+ WrapperResult wrapAndSend(ByteBuffer src) throws IOException {
274+ return wrapAndSendX(src, false);
275+ }
276+
277+ WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException {
278+ if (closed && !ignoreClose) {
279+ throw new IOException ("Engine is closed");
280+ }
281+ Status status;
282+ WrapperResult r = new WrapperResult();
283+ synchronized (wrapLock) {
284+ wrap_dst.clear();
285+ do {
286+ r.result = engine.wrap (src, wrap_dst);
287+ status = r.result.getStatus();
288+ if (status == Status.BUFFER_OVERFLOW) {
289+ wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
290+ }
291+ } while (status == Status.BUFFER_OVERFLOW);
292+ if (status == Status.CLOSED && !ignoreClose) {
293+ closed = true;
294+ return r;
295+ }
296+ if (r.result.bytesProduced() > 0) {
297+ wrap_dst.flip();
298+ int l = wrap_dst.remaining();
299+ assert l == r.result.bytesProduced();
300+ while (l>0) {
301+ l -= chan.write (wrap_dst);
302+ }
303+ }
304+ }
305+ return r;
306+ }
307+
308+ /* block until a complete message is available and return it
309+ * in dst, together with the Result. dst may have been re-allocated
310+ * so caller should check the returned value in Result
311+ * If handshaking is in progress then, possibly no data is returned
312+ */
313+ WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
314+ Status status = Status.OK;
315+ WrapperResult r = new WrapperResult();
316+ r.buf = dst;
317+ if (closed) {
318+ throw new IOException ("Engine is closed");
319+ }
320+ boolean needData;
321+ if (u_remaining > 0) {
322+ unwrap_src.compact();
323+ unwrap_src.flip();
324+ needData = false;
325+ } else {
326+ unwrap_src.clear();
327+ needData = true;
328+ }
329+ synchronized (unwrapLock) {
330+ int x;
331+ do {
332+ if (needData) {
333+ do {
334+ x = chan.read (unwrap_src);
335+ } while (x == 0);
336+ if (x == -1) {
337+ throw new IOException ("connection closed for reading");
338+ }
339+ unwrap_src.flip();
340+ }
341+ r.result = engine.unwrap (unwrap_src, r.buf);
342+ status = r.result.getStatus();
343+ if (status == Status.BUFFER_UNDERFLOW) {
344+ if (unwrap_src.limit() == unwrap_src.capacity()) {
345+ /* buffer not big enough */
346+ unwrap_src = realloc (
347+ unwrap_src, false, BufType.PACKET
348+ );
349+ } else {
350+ /* Buffer not full, just need to read more
351+ * data off the channel. Reset pointers
352+ * for reading off SocketChannel
353+ */
354+ unwrap_src.position (unwrap_src.limit());
355+ unwrap_src.limit (unwrap_src.capacity());
356+ }
357+ needData = true;
358+ } else if (status == Status.BUFFER_OVERFLOW) {
359+ r.buf = realloc (r.buf, true, BufType.APPLICATION);
360+ needData = false;
361+ } else if (status == Status.CLOSED) {
362+ closed = true;
363+ r.buf.flip();
364+ return r;
365+ }
366+ } while (status != Status.OK);
367+ }
368+ u_remaining = unwrap_src.remaining();
369+ return r;
370+ }
371+ }
372+
373+ /**
374+ * send the data in the given ByteBuffer. If a handshake is needed
375+ * then this is handled within this method. When this call returns,
376+ * all of the given user data has been sent and any handshake has been
377+ * completed. Caller should check if engine has been closed.
378+ */
379+ public WrapperResult sendData (ByteBuffer src) throws IOException {
380+ WrapperResult r=null;
381+ while (src.remaining() > 0) {
382+ r = wrapper.wrapAndSend(src);
383+ Status status = r.result.getStatus();
384+ if (status == Status.CLOSED) {
385+ doClosure ();
386+ return r;
387+ }
388+ HandshakeStatus hs_status = r.result.getHandshakeStatus();
389+ if (hs_status != HandshakeStatus.FINISHED &&
390+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
391+ {
392+ doHandshake(hs_status);
393+ }
394+ }
395+ return r;
396+ }
397+
398+ /**
399+ * read data thru the engine into the given ByteBuffer. If the
400+ * given buffer was not large enough, a new one is allocated
401+ * and returned. This call handles handshaking automatically.
402+ * Caller should check if engine has been closed.
403+ */
404+ public WrapperResult recvData (ByteBuffer dst) throws IOException {
405+ /* we wait until some user data arrives */
406+ WrapperResult r = null;
407+ assert dst.position() == 0;
408+ while (dst.position() == 0) {
409+ r = wrapper.recvAndUnwrap (dst);
410+ dst = (r.buf != dst) ? r.buf: dst;
411+ Status status = r.result.getStatus();
412+ if (status == Status.CLOSED) {
413+ doClosure ();
414+ return r;
415+ }
416+
417+ HandshakeStatus hs_status = r.result.getHandshakeStatus();
418+ if (hs_status != HandshakeStatus.FINISHED &&
419+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
420+ {
421+ doHandshake (hs_status);
422+ }
423+ }
424+ dst.flip();
425+ return r;
426+ }
427+
428+ /* we've received a close notify. Need to call wrap to send
429+ * the response
430+ */
431+ void doClosure () throws IOException {
432+ try {
433+ handshaking.lock();
434+ ByteBuffer tmp = allocate(BufType.APPLICATION);
435+ WrapperResult r;
436+ do {
437+ tmp.clear();
438+ tmp.flip ();
439+ r = wrapper.wrapAndSendX (tmp, true);
440+ } while (r.result.getStatus() != Status.CLOSED);
441+ } finally {
442+ handshaking.unlock();
443+ }
444+ }
445+
446+ /* do the (complete) handshake after acquiring the handshake lock.
447+ * If two threads call this at the same time, then we depend
448+ * on the wrapper methods being idempotent. eg. if wrapAndSend()
449+ * is called with no data to send then there must be no problem
450+ */
451+ void doHandshake (HandshakeStatus hs_status) throws IOException {
452+ try {
453+ handshaking.lock();
454+ ByteBuffer tmp = allocate(BufType.APPLICATION);
455+ while (hs_status != HandshakeStatus.FINISHED &&
456+ hs_status != HandshakeStatus.NOT_HANDSHAKING)
457+ {
458+ WrapperResult r = null;
459+ switch (hs_status) {
460+ case NEED_TASK:
461+ Runnable task;
462+ while ((task = engine.getDelegatedTask()) != null) {
463+ /* run in current thread, because we are already
464+ * running an external Executor
465+ */
466+ task.run();
467+ }
468+ /* fall thru - call wrap again */
469+ case NEED_WRAP:
470+ tmp.clear();
471+ tmp.flip();
472+ r = wrapper.wrapAndSend(tmp);
473+ break;
474+
475+ case NEED_UNWRAP:
476+ tmp.clear();
477+ r = wrapper.recvAndUnwrap (tmp);
478+ if (r.buf != tmp) {
479+ tmp = r.buf;
480+ }
481+ assert tmp.position() == 0;
482+ break;
483+ }
484+ hs_status = r.result.getHandshakeStatus();
485+ }
486+ } finally {
487+ handshaking.unlock();
488+ }
489+ }
490+
491+ /**
492+ * represents an SSL input stream. Multiple https requests can
493+ * be sent over one stream. closing this stream causes an SSL close
494+ * input.
495+ */
496+ class InputStream extends java.io.InputStream {
497+
498+ ByteBuffer bbuf;
499+ boolean closed = false;
500+
501+ /* this stream eof */
502+ boolean eof = false;
503+
504+ boolean needData = true;
505+
506+ InputStream () {
507+ bbuf = allocate (BufType.APPLICATION);
508+ }
509+
510+ public int read (byte[] buf, int off, int len) throws IOException {
511+ if (closed) {
512+ throw new IOException ("SSL stream is closed");
513+ }
514+ if (eof) {
515+ return 0;
516+ }
517+ int available=0;
518+ if (!needData) {
519+ available = bbuf.remaining();
520+ needData = (available==0);
521+ }
522+ if (needData) {
523+ bbuf.clear();
524+ WrapperResult r = recvData (bbuf);
525+ bbuf = r.buf== bbuf? bbuf: r.buf;
526+ if ((available=bbuf.remaining()) == 0) {
527+ eof = true;
528+ return 0;
529+ } else {
530+ needData = false;
531+ }
532+ }
533+ /* copy as much as possible from buf into users buf */
534+ if (len > available) {
535+ len = available;
536+ }
537+ bbuf.get (buf, off, len);
538+ return len;
539+ }
540+
541+ public int available () throws IOException {
542+ return bbuf.remaining();
543+ }
544+
545+ public boolean markSupported () {
546+ return false; /* not possible with SSLEngine */
547+ }
548+
549+ public void reset () throws IOException {
550+ throw new IOException ("mark/reset not supported");
551+ }
552+
553+ public long skip (long s) throws IOException {
554+ int n = (int)s;
555+ if (closed) {
556+ throw new IOException ("SSL stream is closed");
557+ }
558+ if (eof) {
559+ return 0;
560+ }
561+ int ret = n;
562+ while (n > 0) {
563+ if (bbuf.remaining() >= n) {
564+ bbuf.position (bbuf.position()+n);
565+ return ret;
566+ } else {
567+ n -= bbuf.remaining();
568+ bbuf.clear();
569+ WrapperResult r = recvData (bbuf);
570+ bbuf = r.buf==bbuf? bbuf: r.buf;
571+ }
572+ }
573+ return ret; /* not reached */
574+ }
575+
576+ /**
577+ * close the SSL connection. All data must have been consumed
578+ * before this is called. Otherwise an exception will be thrown.
579+ * [Note. May need to revisit this. not quite the normal close() symantics
580+ */
581+ public void close () throws IOException {
582+ eof = true;
583+ engine.closeInbound ();
584+ }
585+
586+ public int read (byte[] buf) throws IOException {
587+ return read (buf, 0, buf.length);
588+ }
589+
590+ byte single[] = new byte [1];
591+
592+ public int read () throws IOException {
593+ int n = read (single, 0, 1);
594+ if (n == 0) {
595+ return -1;
596+ } else {
597+ return single[0] & 0xFF;
598+ }
599+ }
600+ }
601+
602+ /**
603+ * represents an SSL output stream. plain text data written to this stream
604+ * is encrypted by the stream. Multiple HTTPS responses can be sent on
605+ * one stream. closing this stream initiates an SSL closure
606+ */
607+ class OutputStream extends java.io.OutputStream {
608+ ByteBuffer buf;
609+ boolean closed = false;
610+ byte single[] = new byte[1];
611+
612+ OutputStream() {
613+ buf = allocate(BufType.APPLICATION);
614+ }
615+
616+ public void write(int b) throws IOException {
617+ single[0] = (byte)b;
618+ write (single, 0, 1);
619+ }
620+
621+ public void write(byte b[]) throws IOException {
622+ write (b, 0, b.length);
623+ }
624+ public void write(byte b[], int off, int len) throws IOException {
625+ if (closed) {
626+ throw new IOException ("output stream is closed");
627+ }
628+ while (len > 0) {
629+ int l = len > buf.capacity() ? buf.capacity() : len;
630+ buf.clear();
631+ buf.put (b, off, l);
632+ len -= l;
633+ off += l;
634+ buf.flip();
635+ WrapperResult r = sendData (buf);
636+ if (r.result.getStatus() == Status.CLOSED) {
637+ closed = true;
638+ if (len > 0) {
639+ throw new IOException ("output stream is closed");
640+ }
641+ }
642+ }
643+ }
644+
645+ public void flush() throws IOException {
646+ /* no-op */
647+ }
648+
649+ public void close() throws IOException {
650+ WrapperResult r=null;
651+ engine.closeOutbound();
652+ closed = true;
653+ HandshakeStatus stat = HandshakeStatus.NEED_WRAP;
654+ buf.clear();
655+ while (stat == HandshakeStatus.NEED_WRAP) {
656+ r = wrapper.wrapAndSend (buf);
657+ stat = r.result.getHandshakeStatus();
658+ }
659+ assert r.result.getStatus() == Status.CLOSED;
660+ }
661+ }
662+}