1 /**
2 	OpenSSL based SSL/TLS stream implementation
3 
4 	Copyright: © 2012-2014 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module vibe.stream.openssl;
9 version(Have_openssl):
10 import vibe.core.log;
11 import vibe.core.net;
12 import vibe.core.stream;
13 import vibe.core.sync;
14 import vibe.stream.tls;
15 import vibe.internal.interfaceproxy : InterfaceProxy;
16 
17 import std.algorithm;
18 import std.array;
19 import std.conv;
20 import std.exception;
21 import std.socket;
22 import std.string;
23 
24 import core.stdc.string : strlen;
25 import core.sync.mutex;
26 import core.thread;
27 
28 /**************************************************************************************************/
29 /* Public types                                                                                   */
30 /**************************************************************************************************/
31 
32 import deimos.openssl.bio;
33 import deimos.openssl.err;
34 import deimos.openssl.rand;
35 import deimos.openssl.ssl;
36 import deimos.openssl.x509v3;
37 
38 version (VibePragmaLib) {
39 	pragma(lib, "ssl");
40 	version (Windows) pragma(lib, "eay");
41 }
42 
43 version (VibeUseOldOpenSSL) private enum haveECDH = false;
44 else private enum haveECDH = OPENSSL_VERSION_NUMBER >= 0x10001000;
45 version(VibeForceALPN) enum alpn_forced = true;
46 else enum alpn_forced = false;
47 enum haveALPN = OPENSSL_VERSION_NUMBER >= 0x10200000 || alpn_forced;
48 
49 // openssl 1.1.0 hack: provides a 1.0.x API in terms of the 1.1.x API
50 version (VibeUseOpenSSL11) {
51 	extern(C) const(SSL_METHOD)* TLS_client_method();
52 
53 	const(SSL_METHOD)* SSLv23_client_method() {
54 		return TLS_client_method();
55 	}
56 
57 	extern(C) const(SSL_METHOD)* TLS_server_method();
58 
59 	const(SSL_METHOD)* SSLv23_server_method() {
60 		return TLS_server_method();
61 	}
62 
63 	// this does nothing in > openssl 1.1.0
64 	void SSL_load_error_strings() {}
65 
66 	extern(C)  int OPENSSL_init_ssl(ulong opts, const void* settings);
67 
68 	// # define SSL_library_init() OPENSSL_init_ssl(0, NULL)
69 	int SSL_library_init() {
70 		return OPENSSL_init_ssl(0, null);
71 	}
72 
73 	//#  define CRYPTO_num_locks()            (1)
74 	int CRYPTO_num_locks() {
75 		return 1;
76 	}
77 
78 	void CRYPTO_set_id_callback(T)(T t) {
79 	}
80 
81 	void CRYPTO_set_locking_callback(T)(T t) {
82 
83 	}
84 
85 	// #define SSL_get_ex_new_index(l, p, newf, dupf, freef) \
86 	//    CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, l, p, newf, dupf, freef)
87 
88 	extern(C) int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
89 	                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
90 	                            CRYPTO_EX_free *free_func);
91 
92 	int SSL_get_ex_new_index(long argl, void *argp,
93 	                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
94 	                            CRYPTO_EX_free *free_func) {
95 		// # define CRYPTO_EX_INDEX_SSL              0
96 		return CRYPTO_get_ex_new_index(0, argl, argp, new_func, dup_func,
97 				free_func);
98 	}
99 
100 	extern(C) BIGNUM* BN_get_rfc3526_prime_2048(BIGNUM *bn);
101 
102 	BIGNUM* get_rfc3526_prime_2048(BIGNUM *bn) {
103 		return BN_get_rfc3526_prime_2048(bn);
104 	}
105 
106 	// #  define sk_num OPENSSL_sk_num
107 	extern(C) int OPENSSL_sk_num(const void *);
108 	extern(C) int sk_num(const(_STACK)* a) {
109 		return OPENSSL_sk_num(a);
110 	}
111 
112 	// #  define sk_value OPENSSL_sk_value
113 	extern(C) void *OPENSSL_sk_value(const void *, int);
114 
115 	extern(C) void* sk_value(const(_STACK)* s, int l) {
116 		return OPENSSL_sk_value(s, l);
117 	}
118 }
119 
120 /**
121 	Creates an SSL/TLS tunnel within an existing stream.
122 
123 	Note: Be sure to call finalize before finalizing/closing the outer stream so that the SSL
124 		tunnel is properly closed first.
125 */
126 final class OpenSSLStream : TLSStream {
127 @safe:
128 
129 	private {
130 		InterfaceProxy!Stream m_stream;
131 		TLSContext m_tlsCtx;
132 		TLSStreamState m_state;
133 		SSLState m_tls;
134 		BIO* m_bio;
135 		ubyte[64] m_peekBuffer;
136 		TLSCertificateInformation m_peerCertificateInfo;
137 		X509* m_peerCertificate;
138 	}
139 
140 	this(InterfaceProxy!Stream underlying, OpenSSLContext ctx, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init, string[] alpn = null)
141 	{
142 		m_stream = underlying;
143 		m_state = state;
144 		m_tlsCtx = ctx;
145 		m_tls = ctx.createClientCtx();
146 		scope (failure) {
147 			() @trusted { SSL_free(m_tls); } ();
148 			m_tls = null;
149 		}
150 
151 		m_bio = () @trusted { return BIO_new(&s_bio_methods); } ();
152 		enforce(m_bio !is null, "SSL failed: failed to create BIO structure.");
153 		m_bio.init_ = 1;
154 		m_bio.ptr = () @trusted { return cast(void*)this; } (); // lifetime is shorter than this, so no GC.addRange needed.
155 		m_bio.shutdown = 0;
156 
157 		() @trusted { SSL_set_bio(m_tls, m_bio, m_bio); } ();
158 
159 		if (state != TLSStreamState.connected) {
160 			OpenSSLContext.VerifyData vdata;
161 			vdata.verifyDepth = ctx.maxCertChainLength;
162 			vdata.validationMode = ctx.peerValidationMode;
163 			vdata.callback = ctx.peerValidationCallback;
164 			vdata.peerName = peer_name;
165 			vdata.peerAddress = peer_address;
166 			() @trusted { SSL_set_ex_data(m_tls, gs_verifyDataIndex, &vdata); } ();
167 			scope (exit) () @trusted { SSL_set_ex_data(m_tls, gs_verifyDataIndex, null); } ();
168 
169 			final switch (state) {
170 				case TLSStreamState.accepting:
171 					//SSL_set_accept_state(m_tls);
172 					checkSSLRet(() @trusted { return SSL_accept(m_tls); } (), "Accepting SSL tunnel");
173 					break;
174 				case TLSStreamState.connecting:
175 					// a client stream can override the default ALPN setting for this context
176 					if (alpn.length) setClientALPN(alpn);
177 					if (peer_name.length)
178 						() @trusted { return SSL_ctrl(m_tls, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, cast(void*)peer_name.toStringz); } ();
179 					//SSL_set_connect_state(m_tls);
180 					checkSSLRet(() @trusted { return SSL_connect(m_tls); } (), "Connecting TLS tunnel");
181 					break;
182 				case TLSStreamState.connected:
183 					break;
184 			}
185 
186 			// ensure that the SSL tunnel gets terminated when an error happens during verification
187 			scope (failure) () @trusted { SSL_shutdown(m_tls); } ();
188 
189 			m_peerCertificate = () @trusted { return SSL_get_peer_certificate(m_tls); } ();
190 			if (m_peerCertificate) {
191 				readPeerCertInfo();
192 				auto result = () @trusted { return SSL_get_verify_result(m_tls); } ();
193 				if (result == X509_V_OK && (ctx.peerValidationMode & TLSPeerValidationMode.checkPeer)) {
194 					if (!verifyCertName(m_peerCertificate, GENERAL_NAME.GEN_DNS, vdata.peerName)) {
195 						version(Windows) import core.sys.windows.winsock2;
196 						else import core.sys.posix.netinet.in_;
197 
198 						logDiagnostic("TLS peer name '%s' couldn't be verified, trying IP address.", vdata.peerName);
199 						char* addr;
200 						int addrlen;
201 						switch (vdata.peerAddress.family) {
202 							default: break;
203 							case AF_INET:
204 								addr = cast(char*)&vdata.peerAddress.sockAddrInet4.sin_addr;
205 								addrlen = vdata.peerAddress.sockAddrInet4.sin_addr.sizeof;
206 								break;
207 							case AF_INET6:
208 								addr = cast(char*)&vdata.peerAddress.sockAddrInet6.sin6_addr;
209 								addrlen = vdata.peerAddress.sockAddrInet6.sin6_addr.sizeof;
210 								break;
211 						}
212 
213 						if (!verifyCertName(m_peerCertificate, GENERAL_NAME.GEN_IPADD, () @trusted { return addr[0 .. addrlen]; } ())) {
214 							logDiagnostic("Error validating TLS peer address");
215 							result = X509_V_ERR_APPLICATION_VERIFICATION;
216 						}
217 					}
218 				}
219 
220 				enforce(result == X509_V_OK, "Peer failed the certificate validation: "~to!string(result));
221 			} //else enforce(ctx.verifyMode < requireCert);
222 		}
223 	}
224 
225 	/** Read certificate info into the clientInformation field */
226 	private void readPeerCertInfo()
227 	{
228 		X509_NAME* name = () @trusted { return X509_get_subject_name(m_peerCertificate); } ();
229 
230 		int c = () @trusted { return X509_NAME_entry_count(name); } ();
231 		foreach (i; 0 .. c) {
232 			X509_NAME_ENTRY *e = () @trusted { return X509_NAME_get_entry(name, i); } ();
233 			ASN1_OBJECT *obj = () @trusted { return X509_NAME_ENTRY_get_object(e); } ();
234 			ASN1_STRING *val = () @trusted { return X509_NAME_ENTRY_get_data(e); } ();
235 
236 			auto longName = () @trusted { return OBJ_nid2ln(OBJ_obj2nid(obj)).to!string; } ();
237 			auto valStr = () @trusted { return cast(string)val.data[0 .. val.length]; } (); // FIXME: .idup?
238 
239 			m_peerCertificateInfo.subjectName.addField(longName, valStr);
240 		}
241 		m_peerCertificateInfo._x509 = m_peerCertificate;
242 	}
243 
244 	~this()
245 	{
246 		if (m_peerCertificate) () @trusted { X509_free(m_peerCertificate); } ();
247 		if (m_tls) () @trusted { SSL_free(m_tls); } ();
248 	}
249 
250 	@property bool empty()
251 	{
252 		return leastSize() == 0;
253 	}
254 
255 	@property ulong leastSize()
256 	{
257 		checkSSLRet(() @trusted { return SSL_peek(m_tls, m_peekBuffer.ptr, 1); } (), "Reading from TLS stream");
258 		return () @trusted { return SSL_pending(m_tls); } ();
259 	}
260 
261 	@property bool dataAvailableForRead()
262 	{
263 		return () @trusted { return SSL_pending(m_tls); } () > 0 || m_stream.dataAvailableForRead;
264 	}
265 
266 	const(ubyte)[] peek()
267 	{
268 		auto ret = checkSSLRet(() @trusted { return SSL_peek(m_tls, m_peekBuffer.ptr, m_peekBuffer.length); } (), "Peeking TLS stream");
269 		return ret > 0 ? m_peekBuffer[0 .. ret] : null;
270 	}
271 
272 	size_t read(scope ubyte[] dst, IOMode mode)
273 	{
274 		size_t nbytes = 0;
275 
276 		while (dst.length > 0) {
277 			int readlen = min(dst.length, int.max);
278 			auto ret = checkSSLRet(() @trusted { return SSL_read(m_tls, dst.ptr, readlen); } (), "Reading from TLS stream");
279 			//logTrace("SSL read %d/%d", ret, dst.length);
280 			dst = dst[ret .. $];
281 			nbytes += ret;
282 
283 			if (mode == IOMode.immediate || mode == IOMode.once)
284 				break;
285 		}
286 
287 		return nbytes;
288 	}
289 
290 	alias read = Stream.read;
291 
292 	size_t write(in ubyte[] bytes_, IOMode mode)
293 	{
294 		const(ubyte)[] bytes = bytes_;
295 
296 		size_t nbytes = 0;
297 
298 		while (bytes.length > 0) {
299 			int writelen = min(bytes.length, int.max);
300 			auto ret = checkSSLRet(() @trusted { return SSL_write(m_tls, bytes.ptr, writelen); } (), "Writing to TLS stream");
301 			//logTrace("SSL write %s", cast(string)bytes[0 .. ret]);
302 			bytes = bytes[ret .. $];
303 			nbytes += ret;
304 
305 			if (mode == IOMode.immediate || mode == IOMode.once)
306 				break;
307 		}
308 
309 		return nbytes;
310 	}
311 
312 	alias write = Stream.write;
313 
314 	void flush()
315 	{
316 		m_stream.flush();
317 	}
318 
319 	void finalize()
320 	{
321 		if( !m_tls ) return;
322 		logTrace("OpenSSLStream finalize");
323 
324 		() @trusted {
325 			SSL_shutdown(m_tls);
326 			SSL_free(m_tls);
327 		} ();
328 
329 		m_tls = null;
330 	}
331 
332 	private int checkSSLRet(int ret, string what)
333 	@safe {
334 		if (ret > 0) return ret;
335 
336 		string desc;
337 		auto err = () @trusted { return SSL_get_error(m_tls, ret); } ();
338 		switch (err) {
339 			default: desc = format("Unknown error (%s)", err); break;
340 			case SSL_ERROR_NONE: desc = "No error"; break;
341 			case SSL_ERROR_ZERO_RETURN: desc = "SSL/TLS tunnel closed"; break;
342 			case SSL_ERROR_WANT_READ: desc = "Need to block for read"; break;
343 			case SSL_ERROR_WANT_WRITE: desc = "Need to block for write"; break;
344 			case SSL_ERROR_WANT_CONNECT: desc = "Need to block for connect"; break;
345 			case SSL_ERROR_WANT_ACCEPT: desc = "Need to block for accept"; break;
346 			case SSL_ERROR_WANT_X509_LOOKUP: desc = "Need to block for certificate lookup"; break;
347 			case SSL_ERROR_SYSCALL:
348 			case SSL_ERROR_SSL:
349 				return enforceSSL(ret, what);
350 		}
351 
352 		const(char)* file = null, data = null;
353 		int line;
354 		int flags;
355 		c_ulong eret;
356 		char[120] ebuf;
357 		while( (eret = () @trusted { return ERR_get_error_line_data(&file, &line, &data, &flags); } ()) != 0 ){
358 			() @trusted { ERR_error_string(eret, ebuf.ptr); } ();
359 			logDebug("%s error at %s:%d: %s (%s)", what,
360 				() @trusted { return to!string(file); } (), line,
361 				() @trusted { return to!string(ebuf.ptr); } (),
362 				flags & ERR_TXT_STRING ? () @trusted { return to!string(data); } () : "-");
363 		}
364 
365 		enforce(ret != 0, format("%s was unsuccessful with ret 0", what));
366 		enforce(ret >= 0, format("%s returned an error: %s", what, desc));
367 		return ret;
368 	}
369 
370 	private int enforceSSL(int ret, string message)
371 	@safe {
372 		if (ret > 0) return ret;
373 
374 		c_ulong eret;
375 		const(char)* file = null, data = null;
376 		int line;
377 		int flags;
378 		string estr;
379 		char[120] ebuf = 0;
380 
381 		while ((eret = () @trusted { return ERR_get_error_line_data(&file, &line, &data, &flags); } ()) != 0) {
382 			() @trusted { ERR_error_string_n(eret, ebuf.ptr, ebuf.length); } ();
383 			estr = () @trusted { return ebuf.ptr.to!string; } ();
384 			// throw the last error code as an exception
385 			logDebug("OpenSSL error at %s:%d: %s (%s)",
386 				() @trusted { return file.to!string; } (), line, estr,
387 				flags & ERR_TXT_STRING ? () @trusted { return to!string(data); } () : "-");
388 			if (!() @trusted { return ERR_peek_error(); } ()) break;
389 		}
390 
391 		throw new Exception(format("%s: %s (%s)", message, estr, eret));
392 	}
393 
394 	@property TLSCertificateInformation peerCertificate()
395 	{
396 		return m_peerCertificateInfo;
397 	}
398 
399 	@property X509* peerCertificateX509()
400 	{
401 		return m_peerCertificate;
402 	}
403 
404 	@property string alpn()
405 	const {
406 		static if (!haveALPN) assert(false, "OpenSSL support not compiled with ALPN enabled. Use VibeForceALPN.");
407 		else {
408 			char[32] data;
409 			uint datalen;
410 
411 			() @trusted { SSL_get0_alpn_selected(m_ssl, cast(const char*) data.ptr, &datalen); } ();
412 			logDebug("alpn selected: ", data.to!string);
413 			if (datalen > 0)
414 				return data[0..datalen].idup;
415 			else return null;
416 		}
417 	}
418 
419 	/// Invoked by client to offer alpn
420 	private void setClientALPN(string[] alpn_list)
421 	{
422 		logDebug("SetClientALPN: ", alpn_list);
423 		import vibe.internal.allocator : dispose, makeArray, vibeThreadAllocator;
424 		ubyte[] alpn;
425 		size_t len;
426 		foreach (string alpn_val; alpn_list)
427 			len += alpn_val.length + 1;
428 		alpn = () @trusted { return vibeThreadAllocator.makeArray!ubyte(len); } ();
429 
430 		size_t i;
431 		foreach (string alpn_val; alpn_list)
432 		{
433 			alpn[i++] = cast(ubyte)alpn_val.length;
434 			alpn[i .. i+alpn_val.length] = cast(immutable(ubyte)[])alpn_val;
435 			i += alpn_val.length;
436 		}
437 		assert(i == len);
438 
439 		static if (haveALPN)
440 			SSL_set_alpn_protos(m_ssl, cast(const char*) alpn.ptr, cast(uint) len);
441 
442 		() @trusted { vibeThreadAllocator.dispose(alpn); } ();
443 	}
444 }
445 
446 
447 /**
448 	Encapsulates the configuration for an SSL tunnel.
449 
450 	Note that when creating an SSLContext with SSLContextKind.client, the
451 	peerValidationMode will be set to SSLPeerValidationMode.trustedCert,
452 	but no trusted certificate authorities are added by default. Use
453 	useTrustedCertificateFile to add those.
454 */
455 final class OpenSSLContext : TLSContext {
456 @safe:
457 
458 	private {
459 		TLSContextKind m_kind;
460 		ssl_ctx_st* m_ctx;
461 		TLSPeerValidationCallback m_peerValidationCallback;
462 		TLSPeerValidationMode m_validationMode;
463 		int m_verifyDepth;
464 		TLSServerNameCallback m_sniCallback;
465 		TLSALPNCallback m_alpnCallback;
466 	}
467 
468 	this(TLSContextKind kind, TLSVersion ver = TLSVersion.any)
469 	{
470 		m_kind = kind;
471 
472 		const(SSL_METHOD)* method;
473 		c_long options = SSL_OP_NO_SSLv2|SSL_OP_NO_COMPRESSION|
474 			SSL_OP_SINGLE_DH_USE|SSL_OP_SINGLE_ECDH_USE;
475 
476 		() @trusted {
477 		final switch (kind) {
478 			case TLSContextKind.client:
479 				final switch (ver) {
480 					case TLSVersion.any: method = SSLv23_client_method(); options |= SSL_OP_NO_SSLv3; break;
481 					case TLSVersion.ssl3: method = SSLv23_client_method(); options |= SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; break;
482 					case TLSVersion.tls1: method = TLSv1_client_method(); break;
483 					//case TLSVersion.tls1_1: method = TLSv1_1_client_method(); break;
484 					//case TLSVersion.tls1_2: method = TLSv1_2_client_method(); break;
485 					case TLSVersion.tls1_1: method = SSLv23_client_method(); options |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; break;
486 					case TLSVersion.tls1_2: method = SSLv23_client_method(); options |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; break;
487 					case TLSVersion.dtls1: method = DTLSv1_client_method(); break;
488 				}
489 				break;
490 			case TLSContextKind.server:
491 			case TLSContextKind.serverSNI:
492 				final switch (ver) {
493 					case TLSVersion.any: method = SSLv23_server_method(); options |= SSL_OP_NO_SSLv3; break;
494 					case TLSVersion.ssl3: method = SSLv23_server_method(); options |= SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; break;
495 					case TLSVersion.tls1: method = TLSv1_server_method(); break;
496 					case TLSVersion.tls1_1: method = SSLv23_server_method(); options |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2; break;
497 					case TLSVersion.tls1_2: method = SSLv23_server_method(); options |= SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; break;
498 					//case TLSVersion.tls1_1: method = TLSv1_1_server_method(); break;
499 					//case TLSVersion.tls1_2: method = TLSv1_2_server_method(); break;
500 					case TLSVersion.dtls1: method = DTLSv1_server_method(); break;
501 				}
502 				options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
503 				break;
504 		}
505 		} ();
506 
507 		m_ctx = () @trusted { return SSL_CTX_new(method); } ();
508 		() @trusted { SSL_CTX_set_options!()(m_ctx, options); }();
509 		if (kind == TLSContextKind.server) {
510 			setDHParams();
511 			static if (haveECDH) setECDHCurve();
512 			guessSessionIDContext();
513 		}
514 
515 		setCipherList();
516 
517 		maxCertChainLength = 9;
518 		if (kind == TLSContextKind.client) peerValidationMode = TLSPeerValidationMode.trustedCert;
519 		else peerValidationMode = TLSPeerValidationMode.none;
520 
521 		// while it would be nice to use the system's certificate store, this
522 		// seems to be difficult to get right across all systems. The most
523 		// popular alternative is to use Mozilla's certificate store and
524 		// distribute it along with the library (e.g. in source code form.
525 
526 		/*version (Posix) {
527 			enforce(SSL_CTX_load_verify_locations(m_ctx, null, "/etc/ssl/certs"),
528 				"Failed to load system certificate store.");
529 		}
530 
531 		version (Windows) {
532 			auto store = CertOpenSystemStore(null, "ROOT");
533 			enforce(store !is null, "Failed to load system certificate store.");
534 			scope (exit) CertCloseStore(store, 0);
535 
536 			PCCERT_CONTEXT ctx;
537 			while((ctx = CertEnumCertificatesInStore(store, ctx)) !is null) {
538 				X509* x509cert;
539 				auto buffer = ctx.pbCertEncoded;
540 				auto len = ctx.cbCertEncoded;
541 				if (ctx.dwCertEncodingType & X509_ASN_ENCODING) {
542 					x509cert = d2i_X509(null, &buffer, len);
543 					X509_STORE_add_cert(SSL_CTX_get_cert_store(m_ctx), x509cert);
544 				}
545 			}
546 		}*/
547 	}
548 
549 	~this()
550 	{
551 		() @trusted { SSL_CTX_free(m_ctx); } ();
552 		m_ctx = null;
553 	}
554 
555 
556 	/// The kind of SSL context (client/server)
557 	@property TLSContextKind kind() const { return m_kind; }
558 
559 	/// Callback function invoked by server to choose alpn
560 	@property void alpnCallback(TLSALPNCallback alpn_chooser)
561 	{
562 		logDebug("Choosing ALPN callback");
563 		m_alpnCallback = alpn_chooser;
564 		static if (haveALPN) {
565 			logDebug("Call select cb");
566 			SSL_CTX_set_alpn_select_cb(m_ctx, &chooser, cast(void*)this);
567 		}
568 	}
569 
570 	/// Get the current ALPN callback function
571 	@property TLSALPNCallback alpnCallback() const { return m_alpnCallback; }
572 
573 	/// Invoked by client to offer alpn
574 	void setClientALPN(string[] alpn_list)
575 	{
576 		static if (!haveALPN) assert(false, "OpenSSL support not compiled with ALPN enabled. Use VibeForceALPN.");
577 		else {
578 			import vibe.utils.memory : allocArray, freeArray, manualAllocator;
579 			ubyte[] alpn;
580 			size_t len;
581 			foreach (string alpn_value; alpn_list)
582 				len += alpn_value.length + 1;
583 			alpn = allocArray!ubyte(manualAllocator(), len);
584 
585 			size_t i;
586 			foreach (string alpn_value; alpn_list)
587 			{
588 				alpn[i++] = cast(ubyte)alpn_value.length;
589 				alpn[i .. i+alpn_value.length] = cast(ubyte[])alpn_value;
590 				i += alpn_value.length;
591 			}
592 			assert(i == len);
593 
594 			SSL_CTX_set_alpn_protos(m_ctx, cast(const char*) alpn.ptr, cast(uint) len);
595 
596 			freeArray(manualAllocator(), alpn);
597 		}
598 	}
599 
600 	/** Specifies the validation level of remote peers.
601 
602 		The default mode for TLSContextKind.client is
603 		TLSPeerValidationMode.trustedCert and the default for
604 		TLSContextKind.server is TLSPeerValidationMode.none.
605 	*/
606 	@property void peerValidationMode(TLSPeerValidationMode mode)
607 	{
608 		m_validationMode = mode;
609 
610 		int sslmode;
611 
612 		with (TLSPeerValidationMode) {
613 			if (mode == none) sslmode = SSL_VERIFY_NONE;
614 			else {
615 				sslmode |= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
616 				if (mode & requireCert) sslmode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
617 			}
618 		}
619 
620 		() @trusted { SSL_CTX_set_verify(m_ctx, sslmode, &verify_callback); } ();
621 	}
622 	/// ditto
623 	@property TLSPeerValidationMode peerValidationMode() const { return m_validationMode; }
624 
625 
626 	/** The maximum length of an accepted certificate chain.
627 
628 		Any certificate chain longer than this will result in the SSL/TLS
629 		negitiation failing.
630 
631 		The default value is 9.
632 	*/
633 	@property void maxCertChainLength(int val)
634 	{
635 		m_verifyDepth = val;
636 		// + 1 to let the validation callback handle the error
637 		() @trusted { SSL_CTX_set_verify_depth(m_ctx, val + 1); } ();
638 	}
639 
640 	/// ditto
641 	@property int maxCertChainLength() const { return m_verifyDepth; }
642 
643 	/** An optional user callback for peer validation.
644 
645 		This callback will be called for each peer and each certificate of
646 		its certificate chain to allow overriding the validation decision
647 		based on the selected peerValidationMode (e.g. to allow invalid
648 		certificates or to reject valid ones). This is mainly useful for
649 		presenting the user with a dialog in case of untrusted or mismatching
650 		certificates.
651 	*/
652 	@property void peerValidationCallback(TLSPeerValidationCallback callback) { m_peerValidationCallback = callback; }
653 	/// ditto
654 	@property inout(TLSPeerValidationCallback) peerValidationCallback() inout { return m_peerValidationCallback; }
655 
656 	@property void sniCallback(TLSServerNameCallback callback)
657 	{
658 		m_sniCallback = callback;
659 		if (m_kind == TLSContextKind.serverSNI) {
660 			() @trusted {
661 				SSL_CTX_callback_ctrl(m_ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast(OSSLCallback)&onContextForServerName);
662 				SSL_CTX_ctrl(m_ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, cast(void*)this);
663 			} ();
664 		}
665 	}
666 	@property inout(TLSServerNameCallback) sniCallback() inout { return m_sniCallback; }
667 
668 	private extern(C) alias OSSLCallback = void function();
669 	private static extern(C) int onContextForServerName(SSL *s, int *ad, void *arg)
670 	{
671 		auto ctx = () @trusted { return cast(OpenSSLContext)arg; } ();
672 		auto servername = () @trusted { return SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); } ();
673 		if (!servername) return SSL_TLSEXT_ERR_NOACK;
674 		auto newctx = cast(OpenSSLContext)ctx.m_sniCallback(() @trusted { return servername.to!string; } ());
675 		if (!newctx) return SSL_TLSEXT_ERR_NOACK;
676 		() @trusted { SSL_set_SSL_CTX(s, newctx.m_ctx); } ();
677 		return SSL_TLSEXT_ERR_OK;
678 	}
679 
680 	OpenSSLStream createStream(InterfaceProxy!Stream underlying, TLSStreamState state, string peer_name = null, NetworkAddress peer_address = NetworkAddress.init)
681 	{
682 		return new OpenSSLStream(underlying, this, state, peer_name, peer_address);
683 	}
684 
685 	/** Set the list of cipher specifications to use for SSL/TLS tunnels.
686 
687 		The list must be a colon separated list of cipher
688 		specifications as accepted by OpenSSL. Calling this function
689 		without argument will restore the default.
690 
691 		See_also: $(LINK https://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT)
692 	*/
693 	void setCipherList(string list = null)
694 		@trusted
695 	{
696 		if (list is null)
697 			SSL_CTX_set_cipher_list(m_ctx,
698 				"ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:"
699 				~ "RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS");
700 		else
701 			SSL_CTX_set_cipher_list(m_ctx, toStringz(list));
702 	}
703 
704 	/** Make up a context ID to assign to the SSL context.
705 
706 		This is required when doing client cert authentication, otherwise many
707 		connections will go aborted as the client tries to revive a session
708 		that it used to have on another machine.
709 
710 		The session ID context should be unique within a pool of servers.
711 		Currently, this is achieved by taking the hostname.
712 	*/
713 	private void guessSessionIDContext()
714 		@trusted
715 	{
716 		string contextID = Socket.hostName;
717 		SSL_CTX_set_session_id_context(m_ctx, cast(ubyte*)contextID.toStringz(), cast(uint)contextID.length);
718 	}
719 
720 	/** Set params to use for DH cipher.
721 	 *
722 	 * By default the 2048-bit prime from RFC 3526 is used.
723 	 *
724 	 * Params:
725 	 * pem_file = Path to a PEM file containing the DH parameters. Calling
726 	 *    this function without argument will restore the default.
727 	 */
728 	void setDHParams(string pem_file=null)
729 	@trusted {
730 		DH* dh;
731 		scope(exit) DH_free(dh);
732 
733 		if (pem_file is null) {
734 			dh = enforce(DH_new(), "Unable to create DH structure.");
735 			dh.p = get_rfc3526_prime_2048(null);
736 			ubyte dh_generator = 2;
737 			dh.g = BN_bin2bn(&dh_generator, dh_generator.sizeof, null);
738 		} else {
739 			import core.stdc.stdio : fclose, fopen;
740 
741 			auto f = enforce(fopen(toStringz(pem_file), "r"), "Failed to load dhparams file "~pem_file);
742 			scope(exit) fclose(f);
743 			dh = enforce(PEM_read_DHparams(f, null, null, null), "Failed to read dhparams file "~pem_file);
744 		}
745 
746 		SSL_CTX_set_tmp_dh(m_ctx, dh);
747 	}
748 
749 	/** Set the elliptic curve to use for ECDH cipher.
750 	 *
751 	 * By default a curve is either chosen automatically or  prime256v1 is used.
752 	 *
753 	 * Params:
754 	 * curve = The short name of the elliptic curve to use. Calling this
755 	 *    function without argument will restore the default.
756 	 *
757 	 */
758 	void setECDHCurve(string curve = null)
759 	@trusted {
760 		static if (haveECDH) {
761 			static if (OPENSSL_VERSION_NUMBER >= 0x10200000) {
762 				// use automatic ecdh curve selection by default
763 				if (curve is null) {
764 					SSL_CTX_set_ecdh_auto(m_ctx, true);
765 					return;
766 				}
767 				// but disable it when an explicit curve is given
768 				SSL_CTX_set_ecdh_auto(m_ctx, false);
769 			}
770 
771 			int nid;
772 			if (curve is null)
773 				nid = NID_X9_62_prime256v1;
774 			else
775 				nid = enforce(OBJ_sn2nid(toStringz(curve)), "Unknown ECDH curve '"~curve~"'.");
776 
777 			auto ecdh = enforce(EC_KEY_new_by_curve_name(nid), "Unable to create ECDH curve.");
778 			SSL_CTX_set_tmp_ecdh(m_ctx, ecdh);
779 			EC_KEY_free(ecdh);
780 		} else assert(false, "ECDH curve selection not available for old versions of OpenSSL");
781 	}
782 
783 	/// Sets a certificate file to use for authenticating to the remote peer
784 	void useCertificateChainFile(string path)
785 	{
786 		enforce(() @trusted { return SSL_CTX_use_certificate_chain_file(m_ctx, toStringz(path)); } (), "Failed to load certificate file " ~ path);
787 	}
788 
789 	/// Sets the private key to use for authenticating to the remote peer based
790 	/// on the configured certificate chain file.
791 	void usePrivateKeyFile(string path)
792 	{
793 		enforce(() @trusted { return SSL_CTX_use_PrivateKey_file(m_ctx, toStringz(path), SSL_FILETYPE_PEM); } (), "Failed to load private key file " ~ path);
794 	}
795 
796 	/** Sets the list of trusted certificates for verifying peer certificates.
797 
798 		If this is a server context, this also entails that the given
799 		certificates are advertised to connecting clients during handshake.
800 
801 		On Linux, the system's root certificate authority list is usually
802 		found at "/etc/ssl/certs/ca-certificates.crt",
803 		"/etc/pki/tls/certs/ca-bundle.crt", or "/etc/ssl/ca-bundle.pem".
804 	*/
805 	void useTrustedCertificateFile(string path)
806 	@trusted {
807 		immutable cPath = toStringz(path);
808 		enforce(SSL_CTX_load_verify_locations(m_ctx, cPath, null),
809 			"Failed to load trusted certificate file " ~ path);
810 
811 		if (m_kind == TLSContextKind.server) {
812 			auto certNames = enforce(SSL_load_client_CA_file(cPath),
813 				"Failed to load client CA name list from file " ~ path);
814 			SSL_CTX_set_client_CA_list(m_ctx, certNames);
815 		}
816 	}
817 
818 	private SSLState createClientCtx()
819 	{
820 		return () @trusted { return SSL_new(m_ctx); } ();
821 	}
822 
823 	private static struct VerifyData {
824 		int verifyDepth;
825 		TLSPeerValidationMode validationMode;
826 		TLSPeerValidationCallback callback;
827 		string peerName;
828 		NetworkAddress peerAddress;
829 	}
830 
831 	private static extern(C) nothrow
832 	int verify_callback(int valid, X509_STORE_CTX* ctx)
833 	@trusted {
834 		X509* err_cert = X509_STORE_CTX_get_current_cert(ctx);
835 		int err = X509_STORE_CTX_get_error(ctx);
836 		int depth = X509_STORE_CTX_get_error_depth(ctx);
837 
838 		SSL* ssl = cast(SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
839 		VerifyData* vdata = cast(VerifyData*)SSL_get_ex_data(ssl, gs_verifyDataIndex);
840 
841 		char[1024] buf;
842 		X509_NAME_oneline(X509_get_subject_name(err_cert), buf.ptr, 256);
843 		buf[$-1] = 0;
844 
845 		try {
846 			logDebug("validate callback for %s", buf.ptr.to!string);
847 
848 			if (depth > vdata.verifyDepth) {
849 				logDiagnostic("SSL cert chain too long: %s vs. %s", depth, vdata.verifyDepth);
850 				valid = false;
851 				err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
852 			}
853 
854 			if (err != X509_V_OK)
855 				logDebug("SSL cert initial error: %s", X509_verify_cert_error_string(err).to!string);
856 
857 			if (!valid) {
858 				switch (err) {
859 					default: break;
860 					case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
861 					case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
862 					case X509_V_ERR_CERT_UNTRUSTED:
863 						assert(ctx.current_cert !is null);
864 						X509_NAME_oneline(X509_get_issuer_name(ctx.current_cert), buf.ptr, buf.length);
865 						buf[$-1] = 0;
866 						logDebug("SSL cert not trusted or unknown issuer: %s", buf.ptr.to!string);
867 						if (!(vdata.validationMode & TLSPeerValidationMode.checkTrust)) {
868 							valid = true;
869 							err = X509_V_OK;
870 						}
871 						break;
872 				}
873 			}
874 
875 			if (!(vdata.validationMode & TLSPeerValidationMode.checkCert)) {
876 				valid = true;
877 				err = X509_V_OK;
878 			}
879 
880 			if (vdata.callback) {
881 				TLSPeerValidationData pvdata;
882 				// ...
883 				if (!valid) {
884 					if (vdata.callback(pvdata)) {
885 						valid = true;
886 						err = X509_V_OK;
887 					}
888 				} else {
889 					if (!vdata.callback(pvdata)) {
890 						logDebug("SSL application verification failed");
891 						valid = false;
892 						err = X509_V_ERR_APPLICATION_VERIFICATION;
893 					}
894 				}
895 			}
896 		} catch (Exception e) {
897 			logWarn("SSL verification failed due to exception: %s", e.msg);
898 			err = X509_V_ERR_APPLICATION_VERIFICATION;
899 			valid = false;
900 		}
901 
902 		X509_STORE_CTX_set_error(ctx, err);
903 
904 		logDebug("SSL validation result: %s (%s)", valid, err);
905 
906 		return valid;
907 	}
908 }
909 
910 alias SSLState = ssl_st*;
911 
912 /**************************************************************************************************/
913 /* Private functions                                                                              */
914 /**************************************************************************************************/
915 
916 private {
917 	__gshared InterruptibleTaskMutex[] g_cryptoMutexes;
918 	__gshared int gs_verifyDataIndex;
919 }
920 
921 shared static this()
922 {
923 	logDebug("Initializing OpenSSL...");
924 	SSL_load_error_strings();
925 	SSL_library_init();
926 
927 	g_cryptoMutexes.length = CRYPTO_num_locks();
928 	// TODO: investigate if a normal Mutex is enough - not sure if BIO is called in a locked state
929 	foreach (i; 0 .. g_cryptoMutexes.length)
930 		g_cryptoMutexes[i] = new InterruptibleTaskMutex;
931 	foreach (ref m; g_cryptoMutexes) {
932 		assert(m !is null);
933 	}
934 
935 	CRYPTO_set_id_callback(&onCryptoGetThreadID);
936 	CRYPTO_set_locking_callback(&onCryptoLock);
937 
938 	enforce(RAND_poll(), "Fatal: failed to initialize random number generator entropy (RAND_poll).");
939 	logDebug("... done.");
940 
941 	gs_verifyDataIndex = SSL_get_ex_new_index(0, cast(void*)"VerifyData".ptr, null, null, null);
942 }
943 
944 private bool verifyCertName(X509* cert, int field, in char[] value, bool allow_wildcards = true)
945 @trusted {
946 	bool delegate(in char[]) @safe str_match;
947 
948 	bool check_value(ASN1_STRING* str, int type) {
949 		if (!str.data || !str.length) return false;
950 
951 		if (type > 0) {
952 			if (type != str.type) return 0;
953 			auto strstr = cast(string)str.data[0 .. str.length];
954 			return type == V_ASN1_IA5STRING ? str_match(strstr) : strstr == value;
955 		}
956 
957 		char* utfstr;
958 		auto utflen = ASN1_STRING_to_UTF8(&utfstr, str);
959 		enforce (utflen >= 0, "Error converting ASN1 string to UTF-8.");
960 		scope (exit) OPENSSL_free(utfstr);
961 		return str_match(utfstr[0 .. utflen]);
962 	}
963 
964 	int cnid;
965 	int alt_type;
966 	final switch (field) {
967 		case GENERAL_NAME.GEN_DNS:
968 			cnid = NID_commonName;
969 			alt_type = V_ASN1_IA5STRING;
970 			str_match = allow_wildcards ? s => matchWildcard(value, s) : s => s.icmp(value) == 0;
971 			break;
972 		case GENERAL_NAME.GEN_IPADD:
973 			cnid = 0;
974 			alt_type = V_ASN1_OCTET_STRING;
975 			str_match = s => s == value;
976 			break;
977 	}
978 
979 	if (auto gens = cast(STACK_OF!GENERAL_NAME*)X509_get_ext_d2i(cert, NID_subject_alt_name, null, null)) {
980 		scope(exit) GENERAL_NAMES_free(gens);
981 
982 		foreach (i; 0 .. sk_GENERAL_NAME_num(gens)) {
983 			auto gen = sk_GENERAL_NAME_value(gens, i);
984 			if (gen.type != field) continue;
985 			ASN1_STRING *cstr = field == GENERAL_NAME.GEN_DNS ? gen.d.dNSName : gen.d.iPAddress;
986 			if (check_value(cstr, alt_type)) return true;
987 		}
988 		if (!cnid) return false;
989 	}
990 
991 	X509_NAME* name = X509_get_subject_name(cert);
992 	int i;
993 	while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) {
994 		X509_NAME_ENTRY* ne = X509_NAME_get_entry(name, i);
995 		ASN1_STRING* str = X509_NAME_ENTRY_get_data(ne);
996 		if (check_value(str, -1)) return true;
997 	}
998 
999 	return false;
1000 }
1001 
1002 private bool matchWildcard(const(char)[] str, const(char)[] pattern)
1003 @safe {
1004 	auto strparts = str.split(".");
1005 	auto patternparts = pattern.split(".");
1006 	if (strparts.length != patternparts.length) return false;
1007 
1008 	bool isValidChar(dchar ch) {
1009 		if (ch >= '0' && ch <= '9') return true;
1010 		if (ch >= 'a' && ch <= 'z') return true;
1011 		if (ch >= 'A' && ch <= 'Z') return true;
1012 		if (ch == '-' || ch == '.') return true;
1013 		return false;
1014 	}
1015 
1016 	if (!pattern.all!(c => isValidChar(c) || c == '*') || !str.all!(c => isValidChar(c)))
1017 		return false;
1018 
1019 	foreach (i; 0 .. strparts.length) {
1020 		import std.regex;
1021 		auto p = patternparts[i];
1022 		auto s = strparts[i];
1023 		if (!p.length || !s.length) return false;
1024 		auto rex = "^" ~ std.array.replace(p, "*", "[^.]*") ~ "$";
1025 		if (!match(s, rex)) return false;
1026 	}
1027 	return true;
1028 }
1029 
1030 unittest {
1031 	assert(matchWildcard("www.example.org", "*.example.org"));
1032 	assert(matchWildcard("www.example.org", "*w.example.org"));
1033 	assert(matchWildcard("www.example.org", "w*w.example.org"));
1034 	assert(matchWildcard("www.example.org", "*w*.example.org"));
1035 	assert(matchWildcard("test.abc.example.org", "test.*.example.org"));
1036 	assert(!matchWildcard("test.abc.example.org", "abc.example.org"));
1037 	assert(!matchWildcard("test.abc.example.org", ".abc.example.org"));
1038 	assert(!matchWildcard("abc.example.org", "a.example.org"));
1039 	assert(!matchWildcard("abc.example.org", "bc.example.org"));
1040 	assert(!matchWildcard("abcdexample.org", "abc.example.org"));
1041 }
1042 
1043 
1044 private nothrow @safe extern(C)
1045 {
1046 	import core.stdc.config;
1047 
1048 
1049 	int chooser(SSL* ssl, const(char)** output, ubyte* outlen, const(char) *input_, uint inlen, void* arg) {
1050 		const(char)[] input = () @trusted { return input_[0 .. inlen]; } ();
1051 
1052 		logDebug("Got chooser input: %s", input);
1053 		OpenSSLContext ctx = () @trusted { return cast(OpenSSLContext) arg; } ();
1054 		import vibe.utils.array : AllocAppender, AppenderResetMode;
1055 		size_t i;
1056 		size_t len;
1057 		Appender!(string[]) alpn_list;
1058 		while (i < inlen)
1059 		{
1060 			len = cast(size_t) input[i];
1061 			++i;
1062 			auto proto = input[i .. i+len];
1063 			i += len;
1064 			() @trusted { alpn_list ~= cast(string)proto; } ();
1065 		}
1066 
1067 		string alpn;
1068 
1069 		try { alpn = ctx.m_alpnCallback(alpn_list.data); } catch (Exception e) { }
1070 		if (alpn) {
1071 			i = 0;
1072 			while (i < inlen)
1073 			{
1074 				len = input[i];
1075 				++i;
1076 				auto proto = input[i .. i+len];
1077 				i += len;
1078 				if (proto == alpn) {
1079 					*output = &proto[0];
1080 					*outlen = cast(ubyte) proto.length;
1081 				}
1082 			}
1083 		}
1084 
1085 		if (!output) {
1086 			logError("None of the proposed ALPN were selected: %s / falling back on HTTP/1.1", input);
1087 			enum hdr = "http/1.1";
1088 			*output = &hdr[0];
1089 			*outlen = cast(ubyte)hdr.length;
1090 		}
1091 
1092 		return 0;
1093 	}
1094 
1095 	c_ulong onCryptoGetThreadID()
1096 	{
1097 		try {
1098 			return cast(c_ulong)(cast(size_t)() @trusted { return cast(void*)Thread.getThis(); } () * 0x35d2c57);
1099 		} catch (Exception e) {
1100 			logWarn("OpenSSL: failed to get current thread ID: %s", e.msg);
1101 			return 0;
1102 		}
1103 	}
1104 
1105 	void onCryptoLock(int mode, int n, const(char)* file, int line)
1106 	{
1107 		try {
1108 			enforce(n >= 0 && n < () @trusted { return g_cryptoMutexes; } ().length, "Mutex index out of range.");
1109 			auto mutex = () @trusted { return g_cryptoMutexes[n]; } ();
1110 			assert(mutex !is null);
1111 			if (mode & CRYPTO_LOCK) mutex.lock();
1112 			else mutex.unlock();
1113 		} catch (Exception e) {
1114 			logWarn("OpenSSL: failed to lock/unlock mutex: %s", e.msg);
1115 		}
1116 	}
1117 
1118 	int onBioNew(BIO *b) nothrow
1119 	{
1120 		b.init_ = 0;
1121 		b.num = -1;
1122 		b.ptr = null;
1123 		b.flags = 0;
1124 		return 1;
1125 	}
1126 
1127 	int onBioFree(BIO *b)
1128 	{
1129 		if( !b ) return 0;
1130 		if( b.shutdown ){
1131 			//if( b.init && b.ptr ) b.ptr.stream.free();
1132 			b.init_ = 0;
1133 			b.flags = 0;
1134 			b.ptr = null;
1135 		}
1136 		return 1;
1137 	}
1138 
1139 	int onBioRead(BIO *b, char *outb, int outlen)
1140 	{
1141 		auto stream = () @trusted { return cast(OpenSSLStream)b.ptr; } ();
1142 
1143 		try {
1144 			outlen = min(outlen, stream.m_stream.leastSize);
1145 			stream.m_stream.read(() @trusted { return cast(ubyte[])outb[0 .. outlen]; } ());
1146 		} catch (Exception e) {
1147 			setSSLError("Error reading from underlying stream", e.msg);
1148 			return -1;
1149 		}
1150 		return outlen;
1151 	}
1152 
1153 	int onBioWrite(BIO *b, const(char) *inb, int inlen)
1154 	{
1155 		auto stream = () @trusted { return cast(OpenSSLStream)b.ptr; } ();
1156 		try {
1157 			stream.m_stream.write(() @trusted { return inb[0 .. inlen]; } ());
1158 		} catch (Exception e) {
1159 			setSSLError("Error writing to underlying stream", e.msg);
1160 			return -1;
1161 		}
1162 		return inlen;
1163 	}
1164 
1165 	c_long onBioCtrl(BIO *b, int cmd, c_long num, void *ptr)
1166 	{
1167 		auto stream = () @trusted { return cast(OpenSSLStream)b.ptr; } ();
1168 		c_long ret = 1;
1169 
1170 		switch(cmd){
1171 			case BIO_CTRL_GET_CLOSE: ret = b.shutdown; break;
1172 			case BIO_CTRL_SET_CLOSE:
1173 				logTrace("SSL set close %d", num);
1174 				b.shutdown = cast(int)num;
1175 				break;
1176 			case BIO_CTRL_PENDING:
1177 				try {
1178 					auto sz = stream.m_stream.leastSize; // FIXME: .peek.length should be sufficient here
1179 					return sz <= c_long.max ? cast(c_long)sz : c_long.max;
1180 				} catch( Exception e ){
1181 					setSSLError("Error reading from underlying stream", e.msg);
1182 					return -1;
1183 				}
1184 			case BIO_CTRL_WPENDING: return 0;
1185 			case BIO_CTRL_DUP:
1186 			case BIO_CTRL_FLUSH:
1187 				ret = 1;
1188 				break;
1189 			default:
1190 				ret = 0;
1191 				break;
1192 		}
1193 		return ret;
1194 	}
1195 
1196 	int onBioPuts(BIO *b, const(char) *s)
1197 	{
1198 		return onBioWrite(b, s, cast(int)() @trusted { return strlen(s); } ());
1199 	}
1200 }
1201 
1202 private void setSSLError(string msg, string submsg, int line = __LINE__, string file = __FILE__)
1203 @trusted nothrow {
1204 	import std.string : toStringz;
1205 	ERR_put_error(ERR_LIB_USER, 0, 1, file.toStringz, line);
1206 	ERR_add_error_data(3, msg.toStringz, ": ".ptr, submsg.toStringz);
1207 }
1208 
1209 private BIO_METHOD s_bio_methods = {
1210 	57, "SslStream",
1211 	&onBioWrite,
1212 	&onBioRead,
1213 	&onBioPuts,
1214 	null, // &onBioGets
1215 	&onBioCtrl,
1216 	&onBioNew,
1217 	&onBioFree,
1218 	null, // &onBioCallbackCtrl
1219 };
1220 
1221 private nothrow extern(C):
1222 static if (haveALPN) {
1223 	alias ALPNCallback = int function(SSL *ssl, const(char) **output, ubyte* outlen, const(char) *input, uint inlen, void *arg);
1224 	void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, ALPNCallback cb, void *arg);
1225 	int SSL_set_alpn_protos(SSL *ssl, const char *data, uint len);
1226 	int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const char* protos, uint protos_len);
1227 	void SSL_get0_alpn_selected(const SSL *ssl, const char* data, uint *len);
1228 }
1229 const(ssl_method_st)* TLSv1_2_server_method();