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