1 module hunt.net.secure.conscrypt.ConscryptEngine;
2 
3 // dfmt off
4 version(WITH_HUNT_SECURITY):
5 // dfmt on
6 
7 import hunt.net.secure.conscrypt.ApplicationProtocolSelectorAdapter;
8 import hunt.net.secure.conscrypt.ApplicationProtocolSelector;
9 import hunt.net.secure.conscrypt.AbstractConscryptEngine;
10 import hunt.net.secure.conscrypt.AbstractSessionContext;
11 import hunt.net.secure.conscrypt.ActiveSession;
12 import hunt.net.secure.conscrypt.AllocatedBuffer;
13 import hunt.net.secure.conscrypt.ClientSessionContext;
14 import hunt.net.secure.conscrypt.ConscryptSession;
15 import hunt.net.secure.conscrypt.NativeCrypto;
16 import hunt.net.secure.conscrypt.NativeSsl;
17 import hunt.net.secure.conscrypt.NativeSslSession;
18 import hunt.net.secure.conscrypt.OpenSSLKey;
19 import hunt.net.secure.conscrypt.PeerInfoProvider;
20 import hunt.net.secure.conscrypt.SessionSnapshot;
21 import hunt.net.secure.conscrypt.SSLParametersImpl;
22 import hunt.net.secure.conscrypt.SSLNullSession;
23 import hunt.net.secure.conscrypt.SSLUtils;
24 
25 
26 // import hunt.net.ssl.KeyManager;
27 // import hunt.net.ssl.X509KeyManager;
28 // import hunt.net.ssl.X509TrustManager;
29 
30 // import hunt.security.Key;
31 // import hunt.security.cert.X509Certificate;
32 // import hunt.security.x500.X500Principal;
33 
34 import hunt.collection;
35 import hunt.net.Exceptions;
36 
37 import hunt.net.ssl.SSLEngine;
38 import hunt.net.ssl.SSLEngineResult;
39 import hunt.net.ssl.SSLSession;
40 
41 import hunt.Exceptions;
42 import hunt.text.Common;
43 
44 import hunt.logging;
45 
46 import deimos.openssl.ssl;
47 
48 import std.algorithm;
49 import std.conv;
50 import std.format;
51 
52 
53 /**
54  * Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
55 ,
56                                                          SSLParametersImpl.AliasChooser,
57                                                          SSLParametersImpl.PSKCallbacks  
58  */
59 final class ConscryptEngine : AbstractConscryptEngine , SSLHandshakeCallbacks {
60     private __gshared SSLEngineResult NEED_UNWRAP_OK;
61     private __gshared SSLEngineResult NEED_UNWRAP_CLOSED;
62     private __gshared SSLEngineResult NEED_WRAP_OK;
63     private __gshared SSLEngineResult NEED_WRAP_CLOSED;
64     private __gshared SSLEngineResult CLOSED_NOT_HANDSHAKING;
65     private __gshared ByteBuffer EMPTY;
66     
67     shared static this()
68     {
69         NEED_UNWRAP_OK = new SSLEngineResult(SSLEngineResult.Status.OK, HandshakeStatus.NEED_UNWRAP, 0, 0);
70         NEED_UNWRAP_CLOSED = new SSLEngineResult(SSLEngineResult.Status.CLOSED, HandshakeStatus.NEED_UNWRAP, 0, 0);
71         NEED_WRAP_OK = new SSLEngineResult(SSLEngineResult.Status.OK, HandshakeStatus.NEED_WRAP, 0, 0);
72         NEED_WRAP_CLOSED = new SSLEngineResult(SSLEngineResult.Status.CLOSED, HandshakeStatus.NEED_WRAP, 0, 0);
73         CLOSED_NOT_HANDSHAKING = new SSLEngineResult(SSLEngineResult.Status.CLOSED, HandshakeStatus.NOT_HANDSHAKING, 0, 0);
74         EMPTY = new HeapByteBuffer(0, 0); // ByteBuffer.allocateDirect(0);
75     }
76 
77     private static BufferAllocator defaultBufferAllocator = null;
78 
79     private SSLParametersImpl sslParameters;
80     private BufferAllocator bufferAllocator;
81 
82     /**
83      * A lazy-created direct buffer used as a bridge between heap buffers provided by the
84      * application and JNI. This avoids the overhead of calling JNI with heap buffers.
85      * Used only when no {@link #bufferAllocator} has been provided.
86      */
87     private ByteBuffer lazyDirectBuffer;
88 
89     /**
90      * Hostname used with the TLS extension SNI hostname.
91      */
92     private string peerHostname;
93 
94     // @GuardedBy("ssl");
95     private int state = EngineStates.STATE_NEW;
96     private bool handshakeFinished;
97 
98     /**
99      * Wrapper around the underlying SSL object.
100      */
101     private NativeSsl ssl;
102 
103     /**
104      * The BIO used for reading/writing encrypted bytes.
105      */
106     // @GuardedBy("ssl");
107     private BioWrapper networkBio;
108 
109     /**
110      * Set during startHandshake.
111      */
112     private ActiveSession activeSession;
113 
114     /**
115      * A snapshot of the active session when the engine was closed.
116      */
117     private SessionSnapshot closedSession;
118 
119     /**
120      * The session object exposed externally from this class.
121      */
122     private SSLSession externalSession;
123 
124     /**
125      * Private key for the TLS Channel ID extension. This field is client-side only. Set during
126      * startHandshake.
127      */
128     // private OpenSSLKey channelIdPrivateKey;
129 
130     private int _maxSealOverhead;
131 
132     /**
133      * Called by the engine when the TLS handshake has completed.
134      */
135     private HandshakeListener handshakeListener;
136 
137     private ByteBuffer[] singleSrcBuffer;
138     private ByteBuffer[] singleDstBuffer;
139     private PeerInfoProvider peerInfoProvider;
140 
141     private SSLException handshakeException;
142 
143     this(SSLParametersImpl sslParameters) {
144         this.sslParameters = sslParameters;
145         peerInfoProvider = PeerInfoProvider.nullProvider();
146         this.ssl = newSsl(sslParameters, this);
147         singleSrcBuffer = new ByteBuffer[1];
148         singleDstBuffer = new ByteBuffer[1];
149         bufferAllocator = defaultBufferAllocator;
150         this.networkBio = ssl.newBio();
151         activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
152         externalSession = this.provideSession();
153 
154         setUseClientMode(sslParameters.getUseClientMode());
155 
156         // externalSession = Platform.wrapSSLSession(new ExternalSession(new Provider() {
157         //     override
158         //     ConscryptSession provideSession() {
159         //         return ConscryptEngine.this.provideSession();
160         //     }
161         // }));
162     }
163 
164     this(string host, int port, SSLParametersImpl sslParameters) {
165         this.sslParameters = sslParameters;
166         this.peerInfoProvider = PeerInfoProvider.forHostAndPort(host, port);
167         this.ssl = newSsl(sslParameters, this);
168         this.networkBio = ssl.newBio();
169         activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
170         setUseClientMode(sslParameters.getUseClientMode());
171     }
172 
173     // this(SSLParametersImpl sslParameters, PeerInfoProvider peerInfoProvider) {
174     //     this.sslParameters = sslParameters;
175     //     this.peerInfoProvider = checkNotNull(peerInfoProvider, "peerInfoProvider");
176     //     this.ssl = newSsl(sslParameters, this);
177     //     this.networkBio = ssl.newBio();
178     //     activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
179     // }
180 
181     private static NativeSsl newSsl(SSLParametersImpl sslParameters, ConscryptEngine engine) {
182         try {
183             return NativeSsl.newInstance(sslParameters, engine);
184         } catch (SSLException e) {
185             throw new RuntimeException(e);
186         }
187     }
188 
189     /**
190      * Configures the default {@link BufferAllocator} to be used by all future
191      * {@link SSLEngine} instances from this provider.
192      */
193     // static void setDefaultBufferAllocator(BufferAllocator bufferAllocator) {
194     //     defaultBufferAllocator = bufferAllocator;
195     // }
196 
197     // override
198     // void setBufferAllocator(BufferAllocator bufferAllocator) {
199     //     synchronized (ssl) {
200     //         if (isHandshakeStarted()) {
201     //             throw new IllegalStateException(
202     //                     "Could not set buffer allocator after the initial handshake has begun.");
203     //         }
204     //         this.bufferAllocator = bufferAllocator;
205     //     }
206     // }
207 
208     /**
209      * Returns the maximum overhead, in bytes, of sealing a record with SSL.
210      */
211     override
212     int maxSealOverhead() {
213         return _maxSealOverhead;
214     }
215 
216     // /**
217     //  * Enables/disables TLS Channel ID for this server engine.
218     //  *
219     //  * <p>This method needs to be invoked before the handshake starts.
220     //  *
221     //  * @throws IllegalStateException if this is a client engine or if the handshake has already
222     //  *         started.
223     //  */
224     // override
225     // void setChannelIdEnabled(bool enabled) {
226     //     synchronized (ssl) {
227     //         if (getUseClientMode()) {
228     //             throw new IllegalStateException("Not allowed in client mode");
229     //         }
230     //         if (isHandshakeStarted()) {
231     //             throw new IllegalStateException(
232     //                     "Could not enable/disable Channel ID after the initial handshake has begun.");
233     //         }
234     //         sslParameters.channelIdEnabled = enabled;
235     //     }
236     // }
237 
238     // /**
239     //  * Gets the TLS Channel ID for this server engine. Channel ID is only available once the
240     //  * handshake completes.
241     //  *
242     //  * @return channel ID or {@code null} if not available.
243     //  *
244     //  * @throws IllegalStateException if this is a client engine or if the handshake has not yet
245     //  * completed.
246     //  * @throws SSLException if channel ID is available but could not be obtained.
247     //  */
248     // override
249     // byte[] getChannelId() {
250     //     synchronized (ssl) {
251     //         if (getUseClientMode()) {
252     //             throw new IllegalStateException("Not allowed in client mode");
253     //         }
254 
255     //         if (isHandshakeStarted()) {
256     //             throw new IllegalStateException(
257     //                     "Channel ID is only available after handshake completes");
258     //         }
259     //         return ssl.getTlsChannelId();
260     //     }
261     // }
262 
263     /**
264      * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
265      *
266      * <p>This method needs to be invoked before the handshake starts.
267      *
268      * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
269      *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
270      *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
271      *
272      * @throws IllegalStateException if this is a server engine or if the handshake has already
273      *         started.
274      */
275     // override
276     // void setChannelIdPrivateKey(PrivateKey privateKey) {
277     //     implementationMissing(false);
278     //     // if (!getUseClientMode()) {
279     //     //     throw new IllegalStateException("Not allowed in server mode");
280     //     // }
281 
282     //     // synchronized (ssl) {
283     //     //     if (isHandshakeStarted()) {
284     //     //         throw new IllegalStateException("Could not change Channel ID private key "
285     //     //                 ~ "after the initial handshake has begun.");
286     //     //     }
287 
288     //     //     if (privateKey is null) {
289     //     //         sslParameters.channelIdEnabled = false;
290     //     //         channelIdPrivateKey = null;
291     //     //         return;
292     //     //     }
293 
294     //     //     sslParameters.channelIdEnabled = true;
295     //     //     try {
296     //     //         ECParameterSpec ecParams = null;
297     //     //         if (privateKey instanceof ECKey) {
298     //     //             ecParams = ((ECKey) privateKey).getParams();
299     //     //         }
300     //     //         if (ecParams is null) {
301     //     //             // Assume this is a P-256 key, as specified in the contract of this method.
302     //     //             ecParams =
303     //     //                     OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec();
304     //     //         }
305     //     //         channelIdPrivateKey =
306     //     //                 OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams);
307     //     //     } catch (InvalidKeyException e) {
308     //     //         // Will have error in startHandshake
309     //     //     }
310     //     // }
311     // }
312 
313     /**
314      * Sets the listener for the completion of the TLS handshake.
315      */
316     override
317     void setHandshakeListener(HandshakeListener handshakeListener) {
318         synchronized (ssl) {
319             if (isHandshakeStarted()) {
320                 throw new IllegalStateException(
321                         "Handshake listener must be set before starting the handshake.");
322             }
323             this.handshakeListener = handshakeListener;
324         }
325     }
326 
327     private bool isHandshakeStarted() {
328         switch (state) {
329             case EngineStates.STATE_NEW:
330             case EngineStates.STATE_MODE_SET:
331                 return false;
332             default:
333                 return true;
334         }
335     }
336 
337     /**
338      * This method enables Server Name Indication (SNI) and overrides the {@link PeerInfoProvider}
339      * supplied during engine creation.  If the hostname is not a valid SNI hostname, the SNI
340      * extension will be omitted from the handshake.
341      */
342     override
343     void setHostname(string hostname) {
344         sslParameters.setUseSni(hostname !is null);
345         this.peerHostname = hostname;
346     }
347 
348     /**
349      * Returns the hostname from {@link #setHostname(string)} or supplied by the
350      * {@link PeerInfoProvider} upon creation. No DNS resolution is attempted before
351      * returning the hostname.
352      */
353     override
354     string getHostname() {
355         return peerHostname !is null ? peerHostname : peerInfoProvider.getHostname();
356     }
357 
358     override
359     string getPeerHost() {
360         return peerHostname !is null ? peerHostname : peerInfoProvider.getHostnameOrIP();
361     }
362 
363     override
364     int getPeerPort() {
365         return peerInfoProvider.getPort();
366     }
367 
368     override
369     void beginHandshake() {
370         synchronized (ssl) {
371             beginHandshakeInternal();
372         }
373     }
374 
375     private void beginHandshakeInternal() {
376         switch (state) {
377             case EngineStates.STATE_NEW: {
378                 throw new IllegalStateException("Client/server mode must be set before handshake");
379             }
380             case EngineStates.STATE_MODE_SET: {
381                 // We know what mode to handshake in but have not started the handshake, proceed
382                 break;
383             }
384             case EngineStates.STATE_CLOSED_INBOUND:
385             case EngineStates.STATE_CLOSED_OUTBOUND:
386             case EngineStates.STATE_CLOSED:
387                 throw new IllegalStateException("Engine has already been closed");
388             default:
389                 // We've already started the handshake, just return
390                 return;
391         }
392 
393         transitionTo(EngineStates.STATE_HANDSHAKE_STARTED);
394 
395         bool releaseResources = true;
396         try {
397             // Prepare the SSL object for the handshake.
398             ssl.initialize(getHostname()); // , channelIdPrivateKey
399 
400             // For clients, offer to resume a previously cached session to avoid the
401             // full TLS handshake.
402             if (getUseClientMode()) {
403                 NativeSslSession cachedSession = clientSessionContext().getCachedSession(
404                         getHostname(), getPeerPort(), sslParameters);
405                 if (cachedSession !is null) {
406                     cachedSession.offerToResume(ssl);
407                 }
408             }
409 
410             _maxSealOverhead = ssl.getMaxSealOverhead();
411             handshake();
412             releaseResources = false;
413         } catch (IOException e) {
414             // Write CCS errors to EventLog
415             string message = e.msg;
416             // Must match error reason string of SSL_R_UNEXPECTED_CCS (in ssl/ssl_err.c)
417             if (message.canFind("unexpected CCS")) {
418                 errorf("ssl_unexpected_ccs: host=%s", getPeerHost());
419             }
420             throw SSLUtils.toSSLHandshakeException(e);
421         } finally {
422             if (releaseResources) {
423                 closeAndFreeResources();
424             }
425         }
426     }
427 
428     override
429     void closeInbound() {
430         synchronized (ssl) {
431             if (state == EngineStates.STATE_CLOSED || state == EngineStates.STATE_CLOSED_INBOUND) {
432                 return;
433             }
434             if (isOutboundDone()) {
435                 transitionTo(EngineStates.STATE_CLOSED);
436             } else {
437                 transitionTo(EngineStates.STATE_CLOSED_INBOUND);
438             }
439         }
440     }
441 
442     override
443     void closeOutbound() {
444         synchronized (ssl) {
445             if (state == EngineStates.STATE_CLOSED || state == EngineStates.STATE_CLOSED_OUTBOUND) {
446                 return;
447             }
448             if (isHandshakeStarted()) {
449                 sendSSLShutdown();
450                 if (isInboundDone()) {
451                     closeAndFreeResources();
452                 } else {
453                     transitionTo(EngineStates.STATE_CLOSED_OUTBOUND);
454                 }
455             } else {
456                 // Never started the handshake. Just close now.
457                 closeAndFreeResources();
458             }
459         }
460     }
461 
462     // override
463     // Runnable getDelegatedTask() {
464     //     // This implementation doesn't use any delegated tasks.
465     //     return null;
466     // }
467 
468     override
469     string[] getEnabledCipherSuites() {
470         return sslParameters.getEnabledCipherSuites();
471     }
472 
473     override
474     string[] getEnabledProtocols() {
475         return sslParameters.getEnabledProtocols();
476     }
477 
478     override
479     bool getEnableSessionCreation() {
480         return sslParameters.getEnableSessionCreation();
481     }
482 
483     // override
484     // SSLParameters getSSLParameters() {
485     //     SSLParameters params = super.getSSLParameters();
486     //     Platform.getSSLParameters(params, sslParameters, this);
487     //     return params;
488     // }
489 
490     // override
491     // void setSSLParameters(SSLParameters p) {
492     //     super.setSSLParameters(p);
493     //     Platform.setSSLParameters(p, sslParameters, this);
494     // }
495 
496     override
497     HandshakeStatus getHandshakeStatus() {
498         synchronized (ssl) {
499             return getHandshakeStatusInternal();
500         }
501     }
502 
503     private HandshakeStatus getHandshakeStatusInternal() {
504         if (handshakeFinished) {
505             return HandshakeStatus.NOT_HANDSHAKING;
506         }
507         switch (state) {
508             case EngineStates.STATE_HANDSHAKE_STARTED:
509                 return pendingStatus(pendingOutboundEncryptedBytes());
510             case EngineStates.STATE_HANDSHAKE_COMPLETED:
511                 return HandshakeStatus.NEED_WRAP;
512             case EngineStates.STATE_NEW:
513             case EngineStates.STATE_MODE_SET:
514             case EngineStates.STATE_CLOSED:
515             case EngineStates.STATE_CLOSED_INBOUND:
516             case EngineStates.STATE_CLOSED_OUTBOUND:
517             case EngineStates.STATE_READY:
518             case EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH:
519                 return HandshakeStatus.NOT_HANDSHAKING;
520             default:
521                 break;
522         }
523         throw new IllegalStateException("Unexpected engine state: " ~ state.to!string());
524     }
525 
526     private int pendingOutboundEncryptedBytes() {
527         return networkBio.getPendingWrittenBytes();
528     }
529 
530     private int pendingInboundCleartextBytes() {
531         return ssl.getPendingReadableBytes();
532     }
533 
534     private static HandshakeStatus pendingStatus(int pendingOutboundBytes) {
535         // Depending on if there is something left in the BIO we need to WRAP or UNWRAP
536         return pendingOutboundBytes > 0 ? HandshakeStatus.NEED_WRAP : HandshakeStatus.NEED_UNWRAP;
537     }
538 
539     override
540     bool getNeedClientAuth() {
541         return sslParameters.getNeedClientAuth();
542     }
543 
544     /**
545      * Work-around to allow this method to be called on older versions of Android.
546      */
547     override
548     SSLSession handshakeSession() {
549         implementationMissing(false);
550         return null;
551         // synchronized (ssl) {
552         //     if (state == EngineStates.STATE_HANDSHAKE_STARTED) {
553         //         return Platform.wrapSSLSession(new ExternalSession(new Provider() {
554         //             override
555         //             ConscryptSession provideSession() {
556         //                 return ConscryptEngine.this.provideHandshakeSession();
557         //             }
558         //         }));
559         //     }
560         //     return null;
561         // }
562     }
563 
564     override
565     SSLSession getSession() {
566         return externalSession;
567     }
568 
569     private ConscryptSession provideSession() {
570         synchronized (ssl) {
571             if (state == EngineStates.STATE_CLOSED) {
572                 return closedSession !is null ? closedSession : SSLNullSession.getNullSession();
573             }
574             if (state < EngineStates.STATE_HANDSHAKE_COMPLETED) {
575                 // Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
576                 return SSLNullSession.getNullSession();
577             }
578             return activeSession;
579         }
580     }
581 
582     private ConscryptSession provideHandshakeSession() {
583         synchronized (ssl) {
584             return state == EngineStates.STATE_HANDSHAKE_STARTED ? activeSession
585                 : SSLNullSession.getNullSession();
586         }
587     }
588 
589     override
590     string[] getSupportedCipherSuites() {
591         return NativeCrypto.getSupportedCipherSuites();
592     }
593 
594     override
595     string[] getSupportedProtocols() {
596         return NativeCrypto.getSupportedProtocols();
597     }
598 
599     override
600     bool getUseClientMode() {
601         return sslParameters.getUseClientMode();
602     }
603 
604     override
605     bool getWantClientAuth() {
606         return sslParameters.getWantClientAuth();
607     }
608 
609     override
610     bool isInboundDone() {
611         synchronized (ssl) {
612             return state == EngineStates.STATE_CLOSED || state == EngineStates.STATE_CLOSED_INBOUND
613                     || ssl.wasShutdownReceived();
614         }
615     }
616 
617     override
618     bool isOutboundDone() {
619         synchronized (ssl) {
620             return state == EngineStates.STATE_CLOSED || state == EngineStates.STATE_CLOSED_OUTBOUND || ssl.wasShutdownSent();
621         }
622     }
623 
624     override
625     void setEnabledCipherSuites(string[] suites) {
626         sslParameters.setEnabledCipherSuites(suites);
627     }
628 
629     override
630     void setEnabledProtocols(string[] protocols) {
631         sslParameters.setEnabledProtocols(protocols);
632     }
633 
634     override
635     void setEnableSessionCreation(bool flag) {
636         sslParameters.setEnableSessionCreation(flag);
637     }
638 
639     override
640     void setNeedClientAuth(bool need) {
641         sslParameters.setNeedClientAuth(need);
642     }
643 
644     override
645     void setUseClientMode(bool mode) {
646         synchronized (ssl) {
647             if (isHandshakeStarted()) {
648                 throw new IllegalArgumentException(
649                         "Can not change mode after handshake: state == " ~ state.to!string());
650             }
651             transitionTo(EngineStates.STATE_MODE_SET);
652             sslParameters.setUseClientMode(mode);
653         }
654     }
655 
656     override
657     void setWantClientAuth(bool want) {
658         sslParameters.setWantClientAuth(want);
659     }
660 
661     override
662     SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) {
663         // synchronized (ssl) {
664             try {
665                 return unwrap(makeSingleSrcBuffer(src), makeSingleDstBuffer(dst));
666             } finally {
667                 resetSingleSrcBuffer();
668                 resetSingleDstBuffer();
669             }
670         // }
671     }
672 
673     override
674     SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) {
675         // synchronized (ssl) {
676             try {
677                 return unwrap(makeSingleSrcBuffer(src), dsts);
678             } finally {
679                 resetSingleSrcBuffer();
680             }
681         // }
682     }
683 
684     override
685     SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset,
686             int length) {
687         synchronized (ssl) {
688             try {
689                 return unwrap(makeSingleSrcBuffer(src), 0, 1, dsts, offset, length);
690             } finally {
691                 resetSingleSrcBuffer();
692             }
693         }
694     }
695 
696     override
697     SSLEngineResult unwrap(ByteBuffer[] srcs, ByteBuffer[] dsts) {
698         assert(srcs !is null, "srcs is null");
699         assert(dsts !is null, "dsts is null");
700         return unwrap(srcs, 0, cast(int)srcs.length, dsts, 0, cast(int)dsts.length);
701     }
702 
703     override
704     SSLEngineResult unwrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength,
705             ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
706         assert(srcs !is null, "srcs is null");
707         assert(dsts !is null, "dsts is null");
708         // infof("srcsOffset=%d", srcsOffset);
709         
710         checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, cast(int)srcs.length);
711         checkPositionIndexes(dstsOffset, dstsOffset + dstsLength, cast(int)dsts.length);
712 
713         // Determine the output capacity.
714         int dstLength = calcDstsLength(dsts, dstsOffset, dstsLength);
715         int endOffset = dstsOffset + dstsLength;
716 
717         int srcsEndOffset = srcsOffset + srcsLength;
718         long srcLength = calcSrcsLength(srcs, srcsOffset, srcsEndOffset);
719 
720         synchronized (ssl) {
721             switch (state) {
722                 case EngineStates.STATE_MODE_SET:
723                     // Begin the handshake implicitly.
724                     beginHandshakeInternal();
725                     break;
726                 case EngineStates.STATE_CLOSED_INBOUND:
727                 case EngineStates.STATE_CLOSED:
728                     // If the inbound direction is closed. we can't send anymore.
729                     return new SSLEngineResult(SSLEngineResult.Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
730                 case EngineStates.STATE_NEW:
731                     throw new IllegalStateException(
732                             "Client/server mode must be set before calling unwrap");
733                 default:
734                     break;
735             }
736 
737             HandshakeStatus handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
738             if (!handshakeFinished) {
739                 handshakeStatus = handshake();
740                 version(HUNT_DEBUG_MORE) trace("handshakeStatus=", handshakeStatus);
741                 if (handshakeStatus == HandshakeStatus.NEED_WRAP) {
742                     return NEED_WRAP_OK;
743                 }
744                 if (state == EngineStates.STATE_CLOSED) {
745                     return NEED_WRAP_CLOSED;
746                 }
747                 // NEED_UNWRAP - just fall through to perform the unwrap.
748             }
749 
750             // Consume any source data. Skip this if there are unread cleartext data.
751             bool noCleartextDataAvailable = pendingInboundCleartextBytes() <= 0;
752             // tracef("srcLength=%d, noCleartextDataAvailable=%s", srcLength, noCleartextDataAvailable);
753             int lenRemaining = 0;
754             if (srcLength > 0 && noCleartextDataAvailable) {
755                 if (srcLength < SSL3_RT_HEADER_LENGTH) {
756                     // Need to be able to read a full TLS header.
757                     return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
758                 }
759 
760                 int packetLength = SSLUtils.getEncryptedPacketLength(srcs, srcsOffset);
761 
762                 version(HUNT_DEBUG_MORE) {
763                     tracef("srcLength=%d, srcsOffset=%d packetLength=%d", srcLength, srcsOffset, packetLength);
764                 }
765 
766 
767                 if (packetLength < 0) {
768                     throw new SSLException("Unable to parse TLS packet header");
769                 }
770 
771                 if (srcLength < packetLength) {
772                     // We either have not enough data to read the packet header or not enough for
773                     // reading the whole packet.
774                     return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
775                 }
776 
777                 // Limit the amount of data to be read to a single packet.
778                 lenRemaining = packetLength;
779             } else if (noCleartextDataAvailable) {
780                 // No pending data and nothing provided as input.  Need more data.
781                 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
782             }
783 
784             // tracef("lenRemaining=%d, srcsOffset=%d, srcsEndOffset=%d", lenRemaining, srcsOffset, srcsEndOffset);
785 
786             // Write all of the encrypted source data to the networkBio
787             int bytesConsumed = 0;
788             if (lenRemaining > 0 && srcsOffset < srcsEndOffset) {
789                 do {
790                     ByteBuffer src = srcs[srcsOffset];
791                     int remaining = src.remaining();
792                     if (remaining == 0) {
793                         // We must skip empty buffers as BIO_write will return 0 if asked to
794                         // write something with length 0.
795                         srcsOffset++;
796                         continue;
797                     }
798                     
799                     // Write the source encrypted data to the networkBio.
800                     int written = writeEncryptedData(src, min(lenRemaining, remaining));
801                     if (written > 0) {
802                         bytesConsumed += written;
803                         lenRemaining -= written;
804                         if (lenRemaining == 0) {
805                             // A whole packet has been consumed.
806                             break;
807                         }
808 
809                         if (written == remaining) {
810                             srcsOffset++;
811                         } else {
812                             // We were not able to write everything into the BIO so break the
813                             // write loop as otherwise we will produce an error on the next
814                             // write attempt, which will trigger a SSL.clearError() later.
815                             break;
816                         }
817                     } else {
818                         // BIO_write returned a negative or zero number, this means we could not
819                         // complete the write operation and should retry later.
820                         // We ignore BIO_* errors here as we use in memory BIO anyway and will
821                         // do another SSL_* call later on in which we will produce an exception
822                         // in case of an error
823                         NativeCrypto.SSL_clear_error();
824                         break;
825                     }
826                 } while (srcsOffset < srcsEndOffset);
827             }
828 
829             // Now read any available plaintext data.
830             int bytesProduced = 0;
831             try {
832                 if (dstLength > 0) {
833                     // Write decrypted data to dsts buffers
834                     for (int idx = dstsOffset; idx < endOffset; ++idx) {
835                         ByteBuffer dst = dsts[idx];
836                         if (!dst.hasRemaining()) {
837                             continue;
838                         }
839 
840                         int bytesRead = readPlaintextData(dst);
841                         if (bytesRead > 0) {
842                             bytesProduced += bytesRead;
843                             if (dst.hasRemaining()) {
844                                 // We haven't filled this buffer fully, break out of the loop
845                                 // and determine the correct response status below.
846                                 break;
847                             }
848                         } else {
849                             switch (bytesRead) {
850                                 case -SSL_ERROR_WANT_READ:
851                                 case -SSL_ERROR_WANT_WRITE: {
852                                     return newResult(bytesConsumed, bytesProduced, handshakeStatus);
853                                 }
854                                 case -SSL_ERROR_ZERO_RETURN: {
855                                     // We received a close_notify from the peer, so mark the
856                                     // inbound direction as closed and shut down the SSL object
857                                     closeInbound();
858                                     sendSSLShutdown();
859                                     return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
860                                             pendingOutboundEncryptedBytes() > 0
861                                                     ? HandshakeStatus.NEED_WRAP : HandshakeStatus.NOT_HANDSHAKING,
862                                             bytesConsumed, bytesProduced);
863                                 }
864                                 default: {
865                                     // Should never get here.
866                                     sendSSLShutdown();
867                                     throw newSslExceptionWithMessage("SSL_read");
868                                 }
869                             }
870                         }
871                     }
872                 } else {
873                     // If the capacity of all destination buffers is 0 we need to trigger a SSL_read
874                     // anyway to ensure everything is flushed in the BIO pair and so we can detect
875                     // it in the pendingInboundCleartextBytes() call.
876                     readPlaintextData(EMPTY);
877                 }
878             } catch (SSLException e) {
879                 if (pendingOutboundEncryptedBytes() > 0) {
880                     // We need to flush any pending bytes to the remote endpoint in case
881                     // there is an alert that needs to be propagated.
882                     if (!handshakeFinished && handshakeException is null) {
883                         // Save the handshake exception. We will re-throw during the next
884                         // handshake.
885                         handshakeException = e;
886                     }
887                     return new SSLEngineResult(SSLEngineResult.Status.OK, HandshakeStatus.NEED_WRAP, bytesConsumed, bytesProduced);
888                 }
889 
890                 // Nothing to write, just shutdown and throw the exception.
891                 sendSSLShutdown();
892                 throw convertException(e);
893             } catch (InterruptedIOException e) {
894                 return newResult(bytesConsumed, bytesProduced, handshakeStatus);
895             } catch (EOFException e) {
896                 closeAll();
897                 throw convertException(e);
898             } catch (IOException e) {
899                 sendSSLShutdown();
900                 throw convertException(e);
901             }
902 
903             // There won't be any application data until we're done handshaking.
904             // We first check handshakeFinished to eliminate the overhead of extra JNI call if
905             // possible.
906             int pendingCleartextBytes = handshakeFinished ? pendingInboundCleartextBytes() : 0;
907             if (pendingCleartextBytes > 0) {
908                 // We filled all buffers but there is still some data pending in the BIO buffer,
909                 // return BUFFER_OVERFLOW.
910                 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
911                         mayFinishHandshake(handshakeStatus == HandshakeStatus.FINISHED
912                                         ? handshakeStatus
913                                         : getHandshakeStatusInternal()),
914                         bytesConsumed, bytesProduced);
915             }
916 
917             return newResult(bytesConsumed, bytesProduced, handshakeStatus);
918         }
919     }
920 
921     private static int calcDstsLength(ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
922         int capacity = 0;
923         for (int i = 0; i < dsts.length; i++) {
924             ByteBuffer dst = dsts[i];
925             assert(dst !is null, format("dsts[%d] is null", i));
926             if (dst.isReadOnly()) {
927                 throw new ReadOnlyBufferException("");
928             }
929             if (i >= dstsOffset && i < dstsOffset + dstsLength) {
930                 capacity += dst.remaining();
931             }
932         }
933         return capacity;
934     }
935 
936     private static long calcSrcsLength(ByteBuffer[] srcs, int srcsOffset, int srcsEndOffset) {
937         long len = 0;
938         for (int i = srcsOffset; i < srcsEndOffset; i++) {
939             ByteBuffer src = srcs[i];
940             if (src is null) {
941                 throw new IllegalArgumentException("srcs[" ~ i.to!string() ~ "] is null");
942             }
943             len += src.remaining();
944         }
945         return len;
946     }
947 
948 
949     // private HandshakeStatus handshake() {
950     //     // try {
951     //         return doHandshake();
952     //     // } catch (Exception e) {
953     //     //     throw SSLUtils.toSSLHandshakeException(e);
954     //     // }
955     // }
956 
957     private HandshakeStatus handshake() {
958         // Only actually perform the handshake if we haven't already just completed it
959         // via BIO operations.
960         try {
961             // First, check to see if we already have a pending alert that needs to be written.
962             if (handshakeException !is null) {
963                 if (pendingOutboundEncryptedBytes() > 0) {
964                     // Need to finish writing the alert to the remote peer.
965                     return HandshakeStatus.NEED_WRAP;
966                 }
967 
968                 // We've finished writing the alert, just throw the exception.
969                 SSLException e = handshakeException;
970                 handshakeException = null;
971                 throw e;
972             }
973 
974             int ssl_error_code = ssl.doHandshake();
975             switch (ssl_error_code) {
976                 case SSL_ERROR_WANT_READ:
977                     return pendingStatus(pendingOutboundEncryptedBytes());
978                 case SSL_ERROR_WANT_WRITE: {
979                     return HandshakeStatus.NEED_WRAP;
980                 }
981                 default: {
982                     // SSL_ERROR_NONE.
983                 }
984             }
985         } catch (SSLException e) {
986             if (pendingOutboundEncryptedBytes() > 0) {
987                 // Delay throwing the exception since we appear to have an outbound alert
988                 // that needs to be written to the remote endpoint.
989                 handshakeException = e;
990                 return HandshakeStatus.NEED_WRAP;
991             }
992 
993             // There is no pending alert to write - just shutdown and throw.
994             sendSSLShutdown();
995             throw e;
996         } catch (IOException e) {
997             sendSSLShutdown();
998             throw e;
999         }
1000 
1001         // The handshake has completed successfully...
1002         version(HUNT_DEBUG_MORE) trace("The handshake is completing...");
1003 
1004         // Update the session from the current state of the SSL object.
1005         activeSession.onPeerCertificateAvailable(getPeerHost(), getPeerPort());
1006 
1007         finishHandshake();
1008         return HandshakeStatus.FINISHED;
1009     }
1010 
1011     private void finishHandshake() {
1012         version(HUNT_DEBUG) info("Handshake finish.");
1013         handshakeFinished = true;
1014         // Notify the listener, if provided.
1015         if (handshakeListener !is null) {
1016             handshakeListener();
1017         }
1018     }
1019 
1020     /**
1021      * Write plaintext data to the OpenSSL internal BIO
1022      *
1023      * Calling this function with src.remaining == 0 is undefined.
1024      */
1025     private int writePlaintextData(ByteBuffer src, int len) {
1026         try {
1027             int pos = src.position();
1028             int sslWrote;
1029             if (src.isDirect()) {
1030                 sslWrote = writePlaintextDataDirect(src, pos, len);
1031             } else {
1032                 sslWrote = writePlaintextDataHeap(src, pos, len);
1033             }
1034             if (sslWrote > 0) {
1035                 src.position(pos + sslWrote);
1036             }
1037             return sslWrote;
1038         } catch (Exception e) {
1039             throw convertException(e);
1040         }
1041     }
1042 
1043     private int writePlaintextDataDirect(ByteBuffer src, int pos, int len) {
1044         return ssl.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
1045     }
1046 
1047     private int writePlaintextDataHeap(ByteBuffer src, int pos, int len) {
1048         AllocatedBuffer allocatedBuffer = null;
1049         try {
1050             ByteBuffer buffer;
1051             if (bufferAllocator !is null) {
1052                 allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
1053                 buffer = allocatedBuffer.nioBuffer();
1054             } else {
1055                 // We don't have a buffer allocator, but we don't want to send a heap
1056                 // buffer to JNI. So lazy-create a direct buffer that we will use from now
1057                 // on to copy plaintext data.
1058                 buffer = getOrCreateLazyDirectBuffer();
1059             }
1060 
1061             // Copy the data to the direct buffer.
1062             int limit = src.limit();
1063             int bytesToWrite = min(len, buffer.remaining());
1064             src.limit(pos + bytesToWrite);
1065             buffer.put(src);
1066             buffer.flip();
1067             // Restore the original position and limit.
1068             src.limit(limit);
1069             src.position(pos);
1070 
1071             return writePlaintextDataDirect(buffer, 0, bytesToWrite);
1072         } finally {
1073             if (allocatedBuffer !is null) {
1074                 // Release the buffer back to the pool.
1075                 allocatedBuffer.release();
1076             }
1077         }
1078     }
1079 
1080     /**
1081      * Read plaintext data from the OpenSSL internal BIO
1082      */
1083     private int readPlaintextData(ByteBuffer dst) {
1084         try {
1085             int pos = dst.position();
1086             int limit = dst.limit();
1087             int len = min(SSL3_RT_MAX_PACKET_SIZE, limit - pos);
1088             if (dst.isDirect()) {
1089                 int bytesRead = readPlaintextDataDirect(dst, pos, len);
1090                 if (bytesRead > 0) {
1091                     dst.position(pos + bytesRead);
1092                 }
1093                 return bytesRead;
1094             }
1095 
1096             // The heap method updates the dst position automatically.
1097             return readPlaintextDataHeap(dst, len);
1098         } catch (CertificateException e) {
1099             throw convertException(e);
1100         }
1101     }
1102 
1103     private int readPlaintextDataDirect(ByteBuffer dst, int pos, int len) {
1104         return ssl.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
1105         // tracef("bbbbbbbbbbb=>%s", dst.toString());
1106         // int r = ssl.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
1107         // byte[] bf = dst.array();
1108         // if(bf.length>16)
1109         // tracef("ccccccccccccc=>%s, %(%02X %)", dst.toString(), bf[0..16]);
1110         // else
1111         // tracef("ccccccccccccc=>%s, %(%02X %)", dst.toString(), bf[0..$]);
1112 
1113         // return r;
1114     }
1115 
1116     private int readPlaintextDataHeap(ByteBuffer dst, int len) {
1117         AllocatedBuffer allocatedBuffer = null;
1118         try {
1119             ByteBuffer buffer;
1120             if (bufferAllocator !is null) {
1121                 allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
1122                 buffer = allocatedBuffer.nioBuffer();
1123             } else {
1124                 // We don't have a buffer allocator, but we don't want to send a heap
1125                 // buffer to JNI. So lazy-create a direct buffer that we will use from now
1126                 // on to copy plaintext data.
1127                 buffer = getOrCreateLazyDirectBuffer();
1128             }
1129 
1130             byte[] bf = buffer.array();
1131             // if(bf.length>16)
1132             // tracef("aaaaaaaaa=>%s, %(%02X %)", buffer.toString(), bf[0..16]);
1133             // else
1134             // tracef("aaaaaaaaa=>%s, %(%02X %)", buffer.toString(), bf[0..$]);
1135 
1136             // Read the data to the direct buffer.
1137             int bytesToRead = min(len, buffer.remaining());
1138             int bytesRead = readPlaintextDataDirect(buffer, 0, bytesToRead);
1139 
1140             // if(bf.length>16)
1141             //     tracef("ccccccccccccc=>bytesRead=%d, %s, %(%02X %)", bytesRead, buffer.toString(), bf[0..16]);
1142 
1143             if (bytesRead > 0) {
1144                 // Copy the data to the heap buffer.
1145                 buffer.position(bytesRead);
1146                 buffer.flip();
1147                 // tracef("ccccccccc, dst=> %s", dst.toString());
1148                 dst.put(buffer);
1149                 // tracef("ddddddddd, dst=> %s", dst.toString());
1150             }
1151 
1152             return bytesRead;
1153         } finally {
1154             if (allocatedBuffer !is null) {
1155                 // Release the buffer back to the pool.
1156                 allocatedBuffer.release();
1157             }
1158         }
1159     }
1160 
1161     private SSLException convertException(Throwable e) {
1162         if (typeid(e) == typeid(SSLHandshakeException) || !handshakeFinished) {
1163             return SSLUtils.toSSLHandshakeException(e);
1164         }
1165         return SSLUtils.toSSLException(e);
1166     }
1167 
1168     /**
1169      * Write encrypted data to the OpenSSL network BIO.
1170      */
1171     private int writeEncryptedData(ByteBuffer src, int len) {
1172         try {
1173             int pos = src.position();
1174             int bytesWritten;
1175             if (src.isDirect()) {
1176                 bytesWritten = writeEncryptedDataDirect(src, pos, len);
1177             } else {
1178                 bytesWritten = writeEncryptedDataHeap(src, pos, len);
1179             }
1180 
1181             if (bytesWritten > 0) {
1182                 src.position(pos + bytesWritten);
1183             }
1184 
1185             return bytesWritten;
1186         } catch (Exception e) {
1187             throw new SSLException("", e);
1188         }
1189     }
1190 
1191     private int writeEncryptedDataDirect(ByteBuffer src, int pos, int len) {
1192         return networkBio.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
1193     }
1194 
1195     private int writeEncryptedDataHeap(ByteBuffer src, int pos, int len) {
1196         AllocatedBuffer allocatedBuffer = null;
1197         try {
1198             ByteBuffer buffer;
1199             if (bufferAllocator !is null) {
1200                 allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
1201                 buffer = allocatedBuffer.nioBuffer();
1202             } else {
1203                 // We don't have a buffer allocator, but we don't want to send a heap
1204                 // buffer to JNI. So lazy-create a direct buffer that we will use from now
1205                 // on to copy encrypted packets.
1206                 buffer = getOrCreateLazyDirectBuffer();
1207             }
1208 
1209             int limit = src.limit();
1210             int bytesToCopy = min(min(limit - pos, len), buffer.remaining());
1211             src.limit(pos + bytesToCopy);
1212             buffer.put(src);
1213             // Restore the original limit.
1214             src.limit(limit);
1215 
1216             // Reset the original position on the source buffer.
1217             src.position(pos);
1218 
1219             int bytesWritten = writeEncryptedDataDirect(buffer, 0, bytesToCopy);
1220 
1221             // Restore the original position.
1222             src.position(pos);
1223 
1224             return bytesWritten;
1225         } finally {
1226             if (allocatedBuffer !is null) {
1227                 // Release the buffer back to the pool.
1228                 allocatedBuffer.release();
1229             }
1230         }
1231     }
1232 
1233     private ByteBuffer getOrCreateLazyDirectBuffer() {
1234         if (lazyDirectBuffer is null) {
1235             int capacity = max(SSL3_RT_MAX_PLAIN_LENGTH, SSL3_RT_MAX_PACKET_SIZE);
1236             lazyDirectBuffer = new HeapByteBuffer(capacity, capacity);
1237             // lazyDirectBuffer = ByteBuffer.allocateDirect(
1238             //         max(SSL3_RT_MAX_PLAIN_LENGTH, SSL3_RT_MAX_PACKET_SIZE));
1239         }
1240         lazyDirectBuffer.clear();
1241         return lazyDirectBuffer;
1242     }
1243 
1244     private long directByteBufferAddress(ByteBuffer directBuffer, int pos) {
1245         byte[] buffer =  directBuffer.array();
1246         // tracef("xxxxxxxxxxxxx=>%s, pos=%d", buffer.ptr, pos);
1247         return cast(long)cast(void*)(buffer.ptr + pos);
1248     }
1249 
1250     private SSLEngineResult readPendingBytesFromBIO(ByteBuffer dst, int bytesConsumed,
1251             int bytesProduced, HandshakeStatus status) {
1252         try {
1253             // Check to see if the engine wrote data into the network BIO
1254             int pendingNet = pendingOutboundEncryptedBytes();
1255             if (pendingNet > 0) {
1256                 // Do we have enough room in dst to write encrypted data?
1257                 int capacity = dst.remaining();
1258                 if (capacity < pendingNet) {
1259                     return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
1260                             mayFinishHandshake( status == HandshakeStatus.FINISHED ? 
1261                                 status : getHandshakeStatus(pendingNet)),
1262                             bytesConsumed, bytesProduced);
1263                 }
1264 
1265                 // Write the pending data from the network BIO into the dst buffer
1266                 int produced = readEncryptedData(dst, pendingNet);
1267 
1268                 if (produced <= 0) {
1269                     warning("Can't read encrypted data.");
1270                     // We ignore BIO_* errors here as we use in memory BIO anyway and will do
1271                     // another SSL_* call later on in which we will produce an exception in
1272                     // case of an error
1273                     NativeCrypto.SSL_clear_error();
1274                 } else {
1275                     bytesProduced += produced;
1276                     pendingNet -= produced;
1277                 }
1278 
1279                 return new SSLEngineResult(getEngineStatus(),
1280                         mayFinishHandshake( status == HandshakeStatus.FINISHED ? 
1281                             status : getHandshakeStatus(pendingNet)),
1282                         bytesConsumed, bytesProduced);
1283             }
1284             return null;
1285         } catch (Exception e) {
1286             throw convertException(e);
1287         }
1288     }
1289 
1290     /**
1291      * Read encrypted data from the OpenSSL network BIO
1292      */
1293     private int readEncryptedData(ByteBuffer dst, int pending) {
1294         try {
1295             int bytesRead = 0;
1296             int pos = dst.position();
1297             if (dst.remaining() >= pending) {
1298                 int limit = dst.limit();
1299                 int len = min(pending, limit - pos);
1300                 if (dst.isDirect()) {
1301                     bytesRead = readEncryptedDataDirect(dst, pos, len);
1302                     // Need to update the position on the dst buffer.
1303                     if (bytesRead > 0) {
1304                         dst.position(pos + bytesRead);
1305                     }
1306                 } else {
1307                     // The heap method will update the position on the dst buffer automatically.
1308                     bytesRead = readEncryptedDataHeap(dst, len);
1309                 }
1310             }
1311 
1312             return bytesRead;
1313         } catch (Exception e) {
1314             throw convertException(e);
1315         }
1316     }
1317 
1318     private int readEncryptedDataDirect(ByteBuffer dst, int pos, int len) {
1319         return networkBio.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
1320     }
1321 
1322     private int readEncryptedDataHeap(ByteBuffer dst, int len) {
1323         AllocatedBuffer allocatedBuffer = null;
1324         try {
1325             ByteBuffer buffer;
1326             if (bufferAllocator !is null) {
1327                 allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
1328                 buffer = allocatedBuffer.nioBuffer();
1329             } else {
1330                 // We don't have a buffer allocator, but we don't want to send a heap
1331                 // buffer to JNI. So lazy-create a direct buffer that we will use from now
1332                 // on to copy encrypted packets.
1333                 buffer = getOrCreateLazyDirectBuffer();
1334             }
1335 
1336             version(HUNT_DEBUG_MORE) trace(BufferUtils.toSummaryString(buffer));
1337 
1338             int bytesToRead = min(len, buffer.remaining());
1339             int bytesRead = readEncryptedDataDirect(buffer, 0, bytesToRead);
1340 
1341             // byte[] temp =  buffer.array();
1342             // tracef("%(%02X %)", temp[0..len]);
1343             version(HUNT_DEBUG_MORE) {
1344                 tracef("read encrypted data: %d / %d bytes", bytesRead, bytesToRead);
1345             }
1346 
1347             if (bytesRead > 0) {
1348                 buffer.position(bytesRead);
1349                 buffer.flip();
1350                 dst.put(buffer);
1351                 version(HUNT_DEBUG_MORE) trace(BufferUtils.toSummaryString(dst));
1352             }
1353 
1354             return bytesRead;
1355         } finally {
1356             if (allocatedBuffer !is null) {
1357                 // Release the buffer back to the pool.
1358                 allocatedBuffer.release();
1359             }
1360         }
1361     }
1362 
1363     private HandshakeStatus mayFinishHandshake(HandshakeStatus status) {
1364         if (!handshakeFinished && status == HandshakeStatus.NOT_HANDSHAKING) {
1365             // If the status was NOT_HANDSHAKING and we not finished the handshake we need to call
1366             // SSL_do_handshake() again
1367             return handshake();
1368         }
1369         return status;
1370     }
1371 
1372     private HandshakeStatus getHandshakeStatus(int pending) {
1373         // Check if we are in the initial handshake phase or shutdown phase
1374         return !handshakeFinished ? pendingStatus(pending) : HandshakeStatus.NOT_HANDSHAKING;
1375     }
1376 
1377     private SSLEngineResult.Status getEngineStatus() {
1378         switch (state) {
1379             case EngineStates.STATE_CLOSED_INBOUND:
1380             case EngineStates.STATE_CLOSED_OUTBOUND:
1381             case EngineStates.STATE_CLOSED:
1382                 return SSLEngineResult.Status.CLOSED;
1383             default:
1384                 return SSLEngineResult.Status.OK;
1385         }
1386     }
1387 
1388     private void closeAll() {
1389         closeOutbound();
1390         closeInbound();
1391     }
1392 
1393     private SSLException newSslExceptionWithMessage(string err) {
1394         if (!handshakeFinished) {
1395             return new SSLException(err);
1396         }
1397         return new SSLHandshakeException(err);
1398     }
1399 
1400     private SSLEngineResult newResult(int bytesConsumed, int bytesProduced,
1401             HandshakeStatus status) {
1402         return new SSLEngineResult(getEngineStatus(),
1403                 mayFinishHandshake(status == HandshakeStatus.FINISHED ? status : getHandshakeStatusInternal()),
1404                 bytesConsumed, bytesProduced);
1405     }
1406 
1407     alias wrap = SSLEngine.wrap;
1408 
1409     override
1410     SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) {
1411         synchronized (ssl) {
1412             try {
1413                 return wrap(makeSingleSrcBuffer(src), dst);
1414             } finally {
1415                 resetSingleSrcBuffer();
1416             }
1417         }
1418     }
1419 
1420     private static string badPositionIndexes(int start, int end, int size) {
1421         if (start < 0 || start > size) {
1422             return badPositionIndex(start, size, "start index");
1423         }
1424         if (end < 0 || end > size) {
1425             return badPositionIndex(end, size, "end index");
1426         }
1427         // end < start
1428         return format("end index (%s) must not be less than start index (%s)", end, start);
1429     }
1430 
1431     private static string badPositionIndex(int index, int size, string desc) {
1432         if (index < 0) {
1433             return format("%s (%s) must not be negative", desc, index);
1434         } else if (size < 0) {
1435             throw new IllegalArgumentException("negative size: " ~ size.to!string());
1436         } else { // index > size
1437             return format("%s (%s) must not be greater than size (%s)", desc, index, size);
1438         }
1439     }
1440 
1441     /**
1442      * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> in an array, list
1443      * or string of size {@code size}, and are in order. A position index may range from zero to
1444      * {@code size}, inclusive.
1445      *
1446      * @param start a user-supplied index identifying a starting position in an array, list or string
1447      * @param end a user-supplied index identifying a ending position in an array, list or string
1448      * @param size the size of that array, list or string
1449      * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size},
1450      *     or if {@code end} is less than {@code start}
1451      * @throws IllegalArgumentException if {@code size} is negative
1452      */
1453     static void checkPositionIndexes(int start, int end, int size) {
1454         // Carefully optimized for execution by hotspot (explanatory comment above)
1455         if (start < 0 || end < start || end > size) {
1456             throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
1457         }
1458     }
1459 
1460     override
1461     SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst)
1462             {
1463         assert(srcs !is null, "srcs is null");
1464         assert(dst !is null, "dst is null");
1465         checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, cast(int)srcs.length);
1466         if (dst.isReadOnly()) {
1467             throw new ReadOnlyBufferException("");
1468         }
1469 
1470         synchronized (ssl) {
1471             switch (state) {
1472                 case EngineStates.STATE_MODE_SET:
1473                     // Begin the handshake implicitly.
1474                     beginHandshakeInternal();
1475                     break;
1476                 case EngineStates.STATE_CLOSED_OUTBOUND:
1477                 case EngineStates.STATE_CLOSED:
1478                     // We may have pending encrypted bytes from a close_notify alert, so
1479                     // try to read them out
1480                     SSLEngineResult pendingNetResult =
1481                             readPendingBytesFromBIO(dst, 0, 0, HandshakeStatus.NOT_HANDSHAKING);
1482                     if (pendingNetResult !is null) {
1483                         return pendingNetResult;
1484                     }
1485                     return new SSLEngineResult(SSLEngineResult.Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
1486                 case EngineStates.STATE_NEW:
1487                     throw new IllegalStateException(
1488                             "Client/server mode must be set before calling wrap");
1489                 default:
1490                     break;
1491             }
1492 
1493             // If we haven't completed the handshake yet, just let the caller know.
1494             HandshakeStatus handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
1495             // Prepare OpenSSL to work in server mode and receive handshake
1496             if (!handshakeFinished) {
1497                 handshakeStatus = handshake();
1498                 if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
1499                     return NEED_UNWRAP_OK;
1500                 }
1501 
1502                 if (state == EngineStates.STATE_CLOSED) {
1503                     return NEED_UNWRAP_CLOSED;
1504                 }
1505                 // NEED_WRAP - just fall through to perform the wrap.
1506             }
1507 
1508             int srcsLen = 0;
1509             int endOffset = srcsOffset + srcsLength;
1510             for (int i = srcsOffset; i < endOffset; ++i) {
1511                 ByteBuffer src = srcs[i];
1512                 if (src is null) {
1513                     throw new IllegalArgumentException("srcs[" ~ i.to!string() ~ "] is null");
1514                 }
1515                 if (srcsLen == SSL3_RT_MAX_PLAIN_LENGTH) {
1516                     continue;
1517                 }
1518 
1519                 srcsLen += src.remaining();
1520                 if (srcsLen > SSL3_RT_MAX_PLAIN_LENGTH || srcsLen < 0) {
1521                     // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to
1522                     // MAX_PLAINTEXT_LENGTH.
1523                     // This also help us to guard against overflow.
1524                     // We not break out here as we still need to check for null entries in srcs[].
1525                     srcsLen = SSL3_RT_MAX_PLAIN_LENGTH;
1526                 }
1527             }
1528 
1529             if (dst.remaining() < SSLUtils.calculateOutNetBufSize(srcsLen)) {
1530                 return new SSLEngineResult(
1531                     SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatusInternal(), 0, 0);
1532             }
1533 
1534             int bytesProduced = 0;
1535             int bytesConsumed = 0;
1536         loop:
1537             for (int i = srcsOffset; i < endOffset; ++i) {
1538                 ByteBuffer src = srcs[i];
1539                 assert(src !is null, format("srcs[%d] is null", i));
1540                 while (src.hasRemaining()) {
1541                     SSLEngineResult pendingNetResult;
1542                     // Write plaintext application data to the SSL engine
1543                     int result = writePlaintextData(src, min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed));
1544                     if (result > 0) {
1545                         bytesConsumed += result;
1546 
1547                         pendingNetResult = readPendingBytesFromBIO(
1548                             dst, bytesConsumed, bytesProduced, handshakeStatus);
1549                         if (pendingNetResult !is null) {
1550                             if (pendingNetResult.getStatus() != SSLEngineResult.Status.OK) {
1551                                 return pendingNetResult;
1552                             }
1553                             bytesProduced = pendingNetResult.bytesProduced();
1554                         }
1555                         if (bytesConsumed == SSL3_RT_MAX_PLAIN_LENGTH) {
1556                             // If we consumed the maximum amount of bytes for the plaintext length
1557                             // break out of the loop and start to fill the dst buffer.
1558                             break loop;
1559                         }
1560                     } else {
1561                         int sslError = ssl.getError(result);
1562                         switch (sslError) {
1563                             case SSL_ERROR_ZERO_RETURN:
1564                                 // This means the connection was shutdown correctly, close inbound
1565                                 // and outbound
1566                                 closeAll();
1567                                 pendingNetResult = readPendingBytesFromBIO(
1568                                         dst, bytesConsumed, bytesProduced, handshakeStatus);
1569                                 return pendingNetResult !is null ? pendingNetResult
1570                                                                 : CLOSED_NOT_HANDSHAKING;
1571                             case SSL_ERROR_WANT_READ:
1572                                 // If there is no pending data to read from BIO we should go back to
1573                                 // event loop and try
1574                                 // to read more data [1]. It is also possible that event loop will
1575                                 // detect the socket
1576                                 // has been closed. [1]
1577                                 // https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
1578                                 pendingNetResult = readPendingBytesFromBIO(
1579                                         dst, bytesConsumed, bytesProduced, handshakeStatus);
1580                                 return pendingNetResult !is null
1581                                         ? pendingNetResult
1582                                         : new SSLEngineResult(getEngineStatus(), HandshakeStatus.NEED_UNWRAP,
1583                                                   bytesConsumed, bytesProduced);
1584                             case SSL_ERROR_WANT_WRITE:
1585                                 // SSL_ERROR_WANT_WRITE typically means that the underlying
1586                                 // transport is not writable
1587                                 // and we should set the "want write" flag on the selector and try
1588                                 // again when the
1589                                 // underlying transport is writable [1]. However we are not directly
1590                                 // writing to the
1591                                 // underlying transport and instead writing to a BIO buffer. The
1592                                 // OpenSsl documentation
1593                                 // says we should do the following [1]:
1594                                 //
1595                                 // "When using a buffering BIO, like a BIO pair, data must be
1596                                 // written into or retrieved
1597                                 // out of the BIO before being able to continue."
1598                                 //
1599                                 // So we attempt to drain the BIO buffer below, but if there is no
1600                                 // data this condition
1601                                 // is undefined and we assume their is a fatal error with the
1602                                 // openssl engine and close.
1603                                 // [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
1604                                 pendingNetResult = readPendingBytesFromBIO(
1605                                         dst, bytesConsumed, bytesProduced, handshakeStatus);
1606                                 return pendingNetResult !is null ? pendingNetResult
1607                                                                 : NEED_WRAP_CLOSED;
1608                             default:
1609                                 // Everything else is considered as error
1610                                 sendSSLShutdown();
1611                                 throw newSslExceptionWithMessage("SSL_write");
1612                         }
1613                     }
1614                 }
1615             }
1616             // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not checked
1617             // if the srcs was
1618             // empty, or only contained empty buffers.
1619             if (bytesConsumed == 0) {
1620                 SSLEngineResult pendingNetResult =
1621                         readPendingBytesFromBIO(dst, 0, bytesProduced, handshakeStatus);
1622                 if (pendingNetResult !is null) {
1623                     return pendingNetResult;
1624                 }
1625             }
1626 
1627             // return new SSLEngineResult(OK, getHandshakeStatusInternal(), bytesConsumed,
1628             // bytesProduced);
1629             return newResult(bytesConsumed, bytesProduced, handshakeStatus);
1630         }
1631     }
1632 
1633     override
1634     int clientPSKKeyRequested(string identityHint, byte[] identity, byte[] key) {
1635         return ssl.clientPSKKeyRequested(identityHint, identity, key);
1636     }
1637 
1638     override
1639     int serverPSKKeyRequested(string identityHint, string identity, byte[] key) {
1640         return ssl.serverPSKKeyRequested(identityHint, identity, key);
1641     }
1642 
1643     override
1644     void onSSLStateChange(int type, int val) {
1645         synchronized (ssl) {
1646             switch (type) {
1647                 case SSL_CB_HANDSHAKE_START: {
1648                     // For clients, this will allow the NEED_UNWRAP status to be
1649                     // returned.
1650                     transitionTo(EngineStates.STATE_HANDSHAKE_STARTED);
1651                     break;
1652                 }
1653                 case SSL_CB_HANDSHAKE_DONE: {
1654                     if (state != EngineStates.STATE_HANDSHAKE_STARTED
1655                             && state != EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH) {
1656                         throw new IllegalStateException("Completed handshake while in mode " ~ state.to!string());
1657                     }
1658                     transitionTo(EngineStates.STATE_HANDSHAKE_COMPLETED);
1659                     break;
1660                 }
1661                 default:
1662                     // Ignore
1663             }
1664         }
1665     }
1666 
1667     override
1668     void onNewSessionEstablished(long sslSessionNativePtr) {
1669 
1670 implementationMissing(false);
1671         // try {
1672         //     // Increment the reference count to "take ownership" of the session resource.
1673         //     NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
1674 
1675         //     // Create a native reference which will release the SSL_SESSION in its finalizer.
1676         //     // This constructor will only throw if the native pointer passed in is NULL, which
1677         //     // BoringSSL guarantees will not happen.
1678         //     NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
1679 
1680         //     NativeSslSession nativeSession = NativeSslSession.newInstance(ref, activeSession);
1681 
1682         //     // Cache the newly established session.
1683         //     AbstractSessionContext ctx = sessionContext();
1684         //     ctx.cacheSession(nativeSession);
1685         // } catch (Exception ignored) {
1686         //     // Ignore.
1687         // }
1688     }
1689 
1690     override
1691     long serverSessionRequested(byte[] id) {
1692         // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
1693         return 0;
1694     }
1695 
1696     override
1697     void verifyCertificateChain(byte[][] certChain, string authMethod) {
1698 implementationMissing(false);
1699         // try {
1700         //     if (certChain is null || certChain.length == 0) {
1701         //         throw new CertificateException("Peer sent no certificate");
1702         //     }
1703         //     X509Certificate[] peerCertChain = SSLUtils.decodeX509CertificateChain(certChain);
1704 
1705         //     X509TrustManager x509tm = sslParameters.getX509TrustManager();
1706         //     if (x509tm is null) {
1707         //         throw new CertificateException("No X.509 TrustManager");
1708         //     }
1709 
1710         //     // Update the peer information on the session.
1711         //     activeSession.onPeerCertificatesReceived(getPeerHost(), getPeerPort(), peerCertChain);
1712 
1713         //     if (getUseClientMode()) {
1714         //         Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
1715         //     } else {
1716         //         string authType = peerCertChain[0].getPublicKey().getAlgorithm();
1717         //         Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
1718         //     }
1719         // } catch (CertificateException e) {
1720         //     throw e;
1721         // } catch (Exception e) {
1722         //     throw new CertificateException(e);
1723         // }
1724     }
1725 
1726     override
1727     void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
1728         ssl.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals);
1729     }
1730 
1731     private void sendSSLShutdown() {
1732         try {
1733             ssl.shutdown();
1734         } catch (IOException ignored) {
1735             // TODO: The RI ignores close failures in SSLSocket, but need to
1736             // investigate whether it does for SSLEngine.
1737         }
1738     }
1739 
1740     private void closeAndFreeResources() {
1741         transitionTo(EngineStates.STATE_CLOSED);
1742         if (!ssl.isClosed()) {
1743             ssl.close();
1744             networkBio.close();
1745         }
1746     }
1747 
1748     // override
1749     // protected void finalize() throws Throwable {
1750     //     try {
1751     //         transitionTo(STATE_CLOSED);
1752     //     } finally {
1753     //         super.finalize();
1754     //     }
1755     // }
1756 
1757     // override string chooseServerAlias(X509KeyManager keyManager, string keyType) {
1758     //     // X509ExtendedKeyManager ekm = cast(X509ExtendedKeyManager) keyManager;
1759     //     // if (ekm is null) {
1760     //     //     return keyManager.chooseServerAlias(keyType, null, null);
1761     //     // } else {
1762     //     //     return ekm.chooseEngineServerAlias(keyType, null, this);
1763     //     // }
1764     //     implementationMissing(false);
1765     //     return "";
1766     // }
1767 
1768     // override string chooseClientAlias(X509KeyManager keyManager, 
1769     //         X500Principal[] issuers, string[] keyTypes) {
1770 
1771     //     implementationMissing(false);
1772     //     return "";
1773     //     // X509ExtendedKeyManager ekm = cast(X509ExtendedKeyManager) keyManager;
1774     //     // if (ekm is null) {
1775     //     //     return keyManager.chooseClientAlias(keyTypes, issuers, null);
1776     //     // } else {
1777     //     //     return ekm.chooseEngineClientAlias(keyTypes, issuers, this);
1778     //     // }
1779     // }
1780 
1781     // override
1782     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
1783     // string chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
1784     //     return keyManager.chooseServerKeyIdentityHint(this);
1785     // }
1786 
1787     // override
1788     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
1789     // string chooseClientPSKIdentity(PSKKeyManager keyManager, string identityHint) {
1790     //     return keyManager.chooseClientKeyIdentity(identityHint, this);
1791     // }
1792 
1793     // override
1794     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
1795     // SecretKey getPSKKey(PSKKeyManager keyManager, string identityHint, string identity) {
1796     //     return keyManager.getKey(identityHint, identity, this);
1797     // }
1798 
1799     /**
1800      * This method enables session ticket support.
1801      *
1802      * @param useSessionTickets True to enable session tickets
1803      */
1804     override
1805     void setUseSessionTickets(bool useSessionTickets) {
1806         sslParameters.setUseSessionTickets(useSessionTickets);
1807     }
1808 
1809     override
1810     string[] getApplicationProtocols() {
1811         return sslParameters.getApplicationProtocols();
1812     }
1813 
1814     override
1815     void setApplicationProtocols(string[] protocols) {
1816         sslParameters.setApplicationProtocols(protocols);
1817     }
1818 
1819     override
1820     void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
1821         setApplicationProtocolSelector(
1822                 selector is null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
1823     }
1824 
1825     // override
1826     // byte[] getTlsUnique() {
1827     //     return ssl.getTlsUnique();
1828     // }
1829 
1830     // override
1831     // void setTokenBindingParams(int... params) {
1832     //     synchronized (ssl) {
1833     //         if (isHandshakeStarted()) {
1834     //             throw new IllegalStateException(
1835     //                     "Cannot set token binding params after handshake has started.");
1836     //         }
1837     //     }
1838     //     ssl.setTokenBindingParams(params);
1839     // };
1840 
1841     // override
1842     // int getTokenBindingParams() {
1843     //     return ssl.getTokenBindingParams();
1844     // }
1845 
1846     // override
1847     // byte[] exportKeyingMaterial(string label, byte[] context, int length) {
1848     //     synchronized (ssl) {
1849     //         if (state < STATE_HANDSHAKE_COMPLETED || state == STATE_CLOSED) {
1850     //             return null;
1851     //         }
1852     //     }
1853     //     return ssl.exportKeyingMaterial(label, context, length);
1854     // }
1855 
1856     void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter adapter) {
1857         sslParameters.setApplicationProtocolSelector(adapter);
1858     }
1859 
1860     override
1861     string getApplicationProtocol() {
1862         return SSLUtils.toProtocolString(ssl.getApplicationProtocol());
1863     }
1864 
1865     override
1866     string getHandshakeApplicationProtocol() {
1867         synchronized (ssl) {
1868             return state == EngineStates.STATE_HANDSHAKE_STARTED ? getApplicationProtocol() : null;
1869         }
1870     }
1871 
1872     private ByteBuffer[] makeSingleSrcBuffer(ByteBuffer src) {
1873         singleSrcBuffer[0] = src;
1874         return singleSrcBuffer;
1875     }
1876 
1877     private void resetSingleSrcBuffer() {
1878         singleSrcBuffer[0] = null;
1879     }
1880 
1881     private ByteBuffer[] makeSingleDstBuffer(ByteBuffer src) {
1882         singleDstBuffer[0] = src;
1883         return singleDstBuffer;
1884     }
1885 
1886     private void resetSingleDstBuffer() {
1887         singleDstBuffer[0] = null;
1888     }
1889 
1890     private ClientSessionContext clientSessionContext() {
1891         return sslParameters.getClientSessionContext();
1892     }
1893 
1894     private AbstractSessionContext sessionContext() {
1895         return sslParameters.getSessionContext();
1896     }
1897 
1898     private void transitionTo(int newState) {
1899         switch (newState) {
1900             case EngineStates.STATE_HANDSHAKE_STARTED: {
1901                 handshakeFinished = false;
1902                 break;
1903             }
1904             case EngineStates.STATE_CLOSED: {
1905                 if (!ssl.isClosed() && state >= EngineStates.STATE_HANDSHAKE_STARTED && 
1906                     state < EngineStates.STATE_CLOSED ) {
1907                     closedSession = new SessionSnapshot(activeSession);
1908                 }
1909                 break;
1910             }
1911             default: {
1912                 break;
1913             }
1914         }
1915 
1916         // Update the state
1917         this.state = newState;
1918     }
1919 }