Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/httpserver/SSLStreams.java
38918 views
/*1* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.httpserver;2627import java.net.*;28import java.nio.*;29import java.io.*;30import java.nio.channels.*;31import java.util.concurrent.locks.*;32import javax.net.ssl.*;33import javax.net.ssl.SSLEngineResult.*;34import com.sun.net.httpserver.*;3536/**37* given a non-blocking SocketChannel, it produces38* (blocking) streams which encrypt/decrypt the SSL content39* and handle the SSL handshaking automatically.40*/4142class SSLStreams {4344SSLContext sslctx;45SocketChannel chan;46TimeSource time;47ServerImpl server;48SSLEngine engine;49EngineWrapper wrapper;50OutputStream os;51InputStream is;5253/* held by thread doing the hand-shake on this connection */54Lock handshaking = new ReentrantLock();5556SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException {57this.server = server;58this.time= (TimeSource)server;59this.sslctx= sslctx;60this.chan= chan;61InetSocketAddress addr =62(InetSocketAddress)chan.socket().getRemoteSocketAddress();63engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort());64engine.setUseClientMode (false);65HttpsConfigurator cfg = server.getHttpsConfigurator();66configureEngine (cfg, addr);67wrapper = new EngineWrapper (chan, engine);68}6970private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){71if (cfg != null) {72Parameters params = new Parameters (cfg, addr);73//BEGIN_TIGER_EXCLUDE74cfg.configure (params);75SSLParameters sslParams = params.getSSLParameters();76if (sslParams != null) {77engine.setSSLParameters (sslParams);78} else79//END_TIGER_EXCLUDE80{81/* tiger compatibility */82if (params.getCipherSuites() != null) {83try {84engine.setEnabledCipherSuites (85params.getCipherSuites()86);87} catch (IllegalArgumentException e) { /* LOG */}88}89engine.setNeedClientAuth (params.getNeedClientAuth());90engine.setWantClientAuth (params.getWantClientAuth());91if (params.getProtocols() != null) {92try {93engine.setEnabledProtocols (94params.getProtocols()95);96} catch (IllegalArgumentException e) { /* LOG */}97}98}99}100}101102class Parameters extends HttpsParameters {103InetSocketAddress addr;104HttpsConfigurator cfg;105106Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {107this.addr = addr;108this.cfg = cfg;109}110public InetSocketAddress getClientAddress () {111return addr;112}113public HttpsConfigurator getHttpsConfigurator() {114return cfg;115}116//BEGIN_TIGER_EXCLUDE117SSLParameters params;118public void setSSLParameters (SSLParameters p) {119params = p;120}121SSLParameters getSSLParameters () {122return params;123}124//END_TIGER_EXCLUDE125}126127/**128* cleanup resources allocated inside this object129*/130void close () throws IOException {131wrapper.close();132}133134/**135* return the SSL InputStream136*/137InputStream getInputStream () throws IOException {138if (is == null) {139is = new InputStream();140}141return is;142}143144/**145* return the SSL OutputStream146*/147OutputStream getOutputStream () throws IOException {148if (os == null) {149os = new OutputStream();150}151return os;152}153154SSLEngine getSSLEngine () {155return engine;156}157158/**159* request the engine to repeat the handshake on this session160* the handshake must be driven by reads/writes on the streams161* Normally, not necessary to call this.162*/163void beginHandshake() throws SSLException {164engine.beginHandshake();165}166167class WrapperResult {168SSLEngineResult result;169170/* if passed in buffer was not big enough then the171* a reallocated buffer is returned here172*/173ByteBuffer buf;174}175176int app_buf_size;177int packet_buf_size;178179enum BufType {180PACKET, APPLICATION181};182183private ByteBuffer allocate (BufType type) {184return allocate (type, -1);185}186187private ByteBuffer allocate (BufType type, int len) {188assert engine != null;189synchronized (this) {190int size;191if (type == BufType.PACKET) {192if (packet_buf_size == 0) {193SSLSession sess = engine.getSession();194packet_buf_size = sess.getPacketBufferSize();195}196if (len > packet_buf_size) {197packet_buf_size = len;198}199size = packet_buf_size;200} else {201if (app_buf_size == 0) {202SSLSession sess = engine.getSession();203app_buf_size = sess.getApplicationBufferSize();204}205if (len > app_buf_size) {206app_buf_size = len;207}208size = app_buf_size;209}210return ByteBuffer.allocate (size);211}212}213214/* reallocates the buffer by :-215* 1. creating a new buffer double the size of the old one216* 2. putting the contents of the old buffer into the new one217* 3. set xx_buf_size to the new size if it was smaller than new size218*219* flip is set to true if the old buffer needs to be flipped220* before it is copied.221*/222private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {223synchronized (this) {224int nsize = 2 * b.capacity();225ByteBuffer n = allocate (type, nsize);226if (flip) {227b.flip();228}229n.put(b);230b = n;231}232return b;233}234/**235* This is a thin wrapper over SSLEngine and the SocketChannel,236* which guarantees the ordering of wraps/unwraps with respect to the underlying237* channel read/writes. It handles the UNDER/OVERFLOW status codes238* It does not handle the handshaking status codes, or the CLOSED status code239* though once the engine is closed, any attempt to read/write to it240* will get an exception. The overall result is returned.241* It functions synchronously/blocking242*/243class EngineWrapper {244245SocketChannel chan;246SSLEngine engine;247Object wrapLock, unwrapLock;248ByteBuffer unwrap_src, wrap_dst;249boolean closed = false;250int u_remaining; // the number of bytes left in unwrap_src after an unwrap()251252EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {253this.chan = chan;254this.engine = engine;255wrapLock = new Object();256unwrapLock = new Object();257unwrap_src = allocate(BufType.PACKET);258wrap_dst = allocate(BufType.PACKET);259}260261void close () throws IOException {262}263264/* try to wrap and send the data in src. Handles OVERFLOW.265* Might block if there is an outbound blockage or if another266* thread is calling wrap(). Also, might not send any data267* if an unwrap is needed.268*/269WrapperResult wrapAndSend(ByteBuffer src) throws IOException {270return wrapAndSendX(src, false);271}272273WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException {274if (closed && !ignoreClose) {275throw new IOException ("Engine is closed");276}277Status status;278WrapperResult r = new WrapperResult();279synchronized (wrapLock) {280wrap_dst.clear();281do {282r.result = engine.wrap (src, wrap_dst);283status = r.result.getStatus();284if (status == Status.BUFFER_OVERFLOW) {285wrap_dst = realloc (wrap_dst, true, BufType.PACKET);286}287} while (status == Status.BUFFER_OVERFLOW);288if (status == Status.CLOSED && !ignoreClose) {289closed = true;290return r;291}292if (r.result.bytesProduced() > 0) {293wrap_dst.flip();294int l = wrap_dst.remaining();295assert l == r.result.bytesProduced();296while (l>0) {297l -= chan.write (wrap_dst);298}299}300}301return r;302}303304/* block until a complete message is available and return it305* in dst, together with the Result. dst may have been re-allocated306* so caller should check the returned value in Result307* If handshaking is in progress then, possibly no data is returned308*/309WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {310Status status = Status.OK;311WrapperResult r = new WrapperResult();312r.buf = dst;313if (closed) {314throw new IOException ("Engine is closed");315}316boolean needData;317if (u_remaining > 0) {318unwrap_src.compact();319unwrap_src.flip();320needData = false;321} else {322unwrap_src.clear();323needData = true;324}325synchronized (unwrapLock) {326int x;327do {328if (needData) {329do {330x = chan.read (unwrap_src);331} while (x == 0);332if (x == -1) {333throw new IOException ("connection closed for reading");334}335unwrap_src.flip();336}337r.result = engine.unwrap (unwrap_src, r.buf);338status = r.result.getStatus();339if (status == Status.BUFFER_UNDERFLOW) {340if (unwrap_src.limit() == unwrap_src.capacity()) {341/* buffer not big enough */342unwrap_src = realloc (343unwrap_src, false, BufType.PACKET344);345} else {346/* Buffer not full, just need to read more347* data off the channel. Reset pointers348* for reading off SocketChannel349*/350unwrap_src.position (unwrap_src.limit());351unwrap_src.limit (unwrap_src.capacity());352}353needData = true;354} else if (status == Status.BUFFER_OVERFLOW) {355r.buf = realloc (r.buf, true, BufType.APPLICATION);356needData = false;357} else if (status == Status.CLOSED) {358closed = true;359r.buf.flip();360return r;361}362} while (status != Status.OK);363}364u_remaining = unwrap_src.remaining();365return r;366}367}368369/**370* send the data in the given ByteBuffer. If a handshake is needed371* then this is handled within this method. When this call returns,372* all of the given user data has been sent and any handshake has been373* completed. Caller should check if engine has been closed.374*/375public WrapperResult sendData (ByteBuffer src) throws IOException {376WrapperResult r=null;377while (src.remaining() > 0) {378r = wrapper.wrapAndSend(src);379Status status = r.result.getStatus();380if (status == Status.CLOSED) {381doClosure ();382return r;383}384HandshakeStatus hs_status = r.result.getHandshakeStatus();385if (hs_status != HandshakeStatus.FINISHED &&386hs_status != HandshakeStatus.NOT_HANDSHAKING)387{388doHandshake(hs_status);389}390}391return r;392}393394/**395* read data thru the engine into the given ByteBuffer. If the396* given buffer was not large enough, a new one is allocated397* and returned. This call handles handshaking automatically.398* Caller should check if engine has been closed.399*/400public WrapperResult recvData (ByteBuffer dst) throws IOException {401/* we wait until some user data arrives */402WrapperResult r = null;403assert dst.position() == 0;404while (dst.position() == 0) {405r = wrapper.recvAndUnwrap (dst);406dst = (r.buf != dst) ? r.buf: dst;407Status status = r.result.getStatus();408if (status == Status.CLOSED) {409doClosure ();410return r;411}412413HandshakeStatus hs_status = r.result.getHandshakeStatus();414if (hs_status != HandshakeStatus.FINISHED &&415hs_status != HandshakeStatus.NOT_HANDSHAKING)416{417doHandshake (hs_status);418}419}420dst.flip();421return r;422}423424/* we've received a close notify. Need to call wrap to send425* the response426*/427void doClosure () throws IOException {428try {429handshaking.lock();430ByteBuffer tmp = allocate(BufType.APPLICATION);431WrapperResult r;432do {433tmp.clear();434tmp.flip ();435r = wrapper.wrapAndSendX (tmp, true);436} while (r.result.getStatus() != Status.CLOSED);437} finally {438handshaking.unlock();439}440}441442/* do the (complete) handshake after acquiring the handshake lock.443* If two threads call this at the same time, then we depend444* on the wrapper methods being idempotent. eg. if wrapAndSend()445* is called with no data to send then there must be no problem446*/447@SuppressWarnings("fallthrough")448void doHandshake (HandshakeStatus hs_status) throws IOException {449try {450handshaking.lock();451ByteBuffer tmp = allocate(BufType.APPLICATION);452while (hs_status != HandshakeStatus.FINISHED &&453hs_status != HandshakeStatus.NOT_HANDSHAKING)454{455WrapperResult r = null;456switch (hs_status) {457case NEED_TASK:458Runnable task;459while ((task = engine.getDelegatedTask()) != null) {460/* run in current thread, because we are already461* running an external Executor462*/463task.run();464}465/* fall thru - call wrap again */466case NEED_WRAP:467tmp.clear();468tmp.flip();469r = wrapper.wrapAndSend(tmp);470break;471472case NEED_UNWRAP:473tmp.clear();474r = wrapper.recvAndUnwrap (tmp);475if (r.buf != tmp) {476tmp = r.buf;477}478assert tmp.position() == 0;479break;480}481hs_status = r.result.getHandshakeStatus();482}483} finally {484handshaking.unlock();485}486}487488/**489* represents an SSL input stream. Multiple https requests can490* be sent over one stream. closing this stream causes an SSL close491* input.492*/493class InputStream extends java.io.InputStream {494495ByteBuffer bbuf;496boolean closed = false;497498/* this stream eof */499boolean eof = false;500501boolean needData = true;502503InputStream () {504bbuf = allocate (BufType.APPLICATION);505}506507public int read (byte[] buf, int off, int len) throws IOException {508if (closed) {509throw new IOException ("SSL stream is closed");510}511if (eof) {512return 0;513}514int available=0;515if (!needData) {516available = bbuf.remaining();517needData = (available==0);518}519if (needData) {520bbuf.clear();521WrapperResult r = recvData (bbuf);522bbuf = r.buf== bbuf? bbuf: r.buf;523if ((available=bbuf.remaining()) == 0) {524eof = true;525return 0;526} else {527needData = false;528}529}530/* copy as much as possible from buf into users buf */531if (len > available) {532len = available;533}534bbuf.get (buf, off, len);535return len;536}537538public int available () throws IOException {539return bbuf.remaining();540}541542public boolean markSupported () {543return false; /* not possible with SSLEngine */544}545546public void reset () throws IOException {547throw new IOException ("mark/reset not supported");548}549550public long skip (long s) throws IOException {551int n = (int)s;552if (closed) {553throw new IOException ("SSL stream is closed");554}555if (eof) {556return 0;557}558int ret = n;559while (n > 0) {560if (bbuf.remaining() >= n) {561bbuf.position (bbuf.position()+n);562return ret;563} else {564n -= bbuf.remaining();565bbuf.clear();566WrapperResult r = recvData (bbuf);567bbuf = r.buf==bbuf? bbuf: r.buf;568}569}570return ret; /* not reached */571}572573/**574* close the SSL connection. All data must have been consumed575* before this is called. Otherwise an exception will be thrown.576* [Note. May need to revisit this. not quite the normal close() symantics577*/578public void close () throws IOException {579eof = true;580engine.closeInbound ();581}582583public int read (byte[] buf) throws IOException {584return read (buf, 0, buf.length);585}586587byte single[] = new byte [1];588589public int read () throws IOException {590int n = read (single, 0, 1);591if (n == 0) {592return -1;593} else {594return single[0] & 0xFF;595}596}597}598599/**600* represents an SSL output stream. plain text data written to this stream601* is encrypted by the stream. Multiple HTTPS responses can be sent on602* one stream. closing this stream initiates an SSL closure603*/604class OutputStream extends java.io.OutputStream {605ByteBuffer buf;606boolean closed = false;607byte single[] = new byte[1];608609OutputStream() {610buf = allocate(BufType.APPLICATION);611}612613public void write(int b) throws IOException {614single[0] = (byte)b;615write (single, 0, 1);616}617618public void write(byte b[]) throws IOException {619write (b, 0, b.length);620}621public void write(byte b[], int off, int len) throws IOException {622if (closed) {623throw new IOException ("output stream is closed");624}625while (len > 0) {626int l = len > buf.capacity() ? buf.capacity() : len;627buf.clear();628buf.put (b, off, l);629len -= l;630off += l;631buf.flip();632WrapperResult r = sendData (buf);633if (r.result.getStatus() == Status.CLOSED) {634closed = true;635if (len > 0) {636throw new IOException ("output stream is closed");637}638}639}640}641642public void flush() throws IOException {643/* no-op */644}645646public void close() throws IOException {647WrapperResult r=null;648engine.closeOutbound();649closed = true;650HandshakeStatus stat = HandshakeStatus.NEED_WRAP;651buf.clear();652while (stat == HandshakeStatus.NEED_WRAP) {653r = wrapper.wrapAndSend (buf);654stat = r.result.getHandshakeStatus();655}656assert r.result.getStatus() == Status.CLOSED;657}658}659}660661662