1 module requests.ssl_adapter; 2 3 import std.stdio; 4 import std.string; 5 import std.format; 6 import std.typecons; 7 import core.stdc.stdlib; 8 import core.sys.posix.dlfcn; 9 import std.experimental.logger; 10 11 version(Windows) { 12 import core.sys.windows.windows; 13 alias DLSYM = GetProcAddress; 14 } else { 15 alias DLSYM = dlsym; 16 } 17 18 /* 19 * /usr/include/openssl/tls1.h:# define TLS_ANY_VERSION 0x10000 20 */ 21 22 immutable int TLS_ANY_VERSION = 0x10000; 23 immutable int TLS1_VERSION = 0x0301; 24 immutable int TLS1_2_VERSION = 0x0303; 25 26 struct SSL {}; 27 struct SSL_CTX {}; 28 struct SSL_METHOD {}; 29 30 // 31 // N - function name, R - return type, A - args 32 // 33 string SSL_Function_decl(string N, R, A...)() { 34 string F = "extern (C) @nogc nothrow %s function %s adapter_%s;".format(R.stringof, A.stringof, N); 35 return F; 36 } 37 string SSL_Function_set_i(string N, R, A...)() { 38 string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libssl, \"%s\");".format(N, N, N); 39 return F; 40 } 41 string CRYPTO_Function_set_i(string N, R, A...)() { 42 string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libcrypto, \"%s\");".format(N, N, N); 43 return F; 44 } 45 46 private alias Version = Tuple!(int, "major", int, "minor"); 47 48 immutable static OpenSSL openssl; 49 50 shared static this() { 51 version(OSX) { 52 enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)"; 53 immutable string[] libsslname = [ 54 "libssl.46.dylib", 55 "libssl.44.dylib", 56 "libssl.43.dylib", 57 "libssl.35.dylib", 58 "libssl.dylib", 59 ]; 60 immutable string[] libcryptoname = [ 61 "libcrypto.44.dylib", 62 "libcrypto.42.dylib", 63 "libcrypto.41.dylib", 64 "libcrypto.35.dylib", 65 "libcrypto.dylib", 66 ]; 67 } else 68 version(linux) { 69 enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)"; 70 immutable string[] libsslname = [ 71 "libssl.so.1.1", 72 "libssl.so.1.0.2", 73 "libssl.so.1.0.1", 74 "libssl.so.1.0.0", 75 "libssl.so", 76 ]; 77 immutable string[] libcryptoname = [ 78 "libcrypto.so.1.1", 79 "libcrypto.so.1.0.2", 80 "libcrypto.so.1.0.1", 81 "libcrypto.so.1.0.0", 82 "libcrypto.so", 83 ]; 84 } else 85 version(FreeBSD) { 86 enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)"; 87 immutable string[] libsslname = [ 88 "libssl.so.1.1", 89 "libssl.so.1.0.2", 90 "libssl.so.1.0.1", 91 "libssl.so.1.0.0", 92 "libssl.so", 93 ]; 94 immutable string[] libcryptoname = [ 95 "libcrypto.so.1.1", 96 "libcrypto.so.1.0.2", 97 "libcrypto.so.1.0.1", 98 "libcrypto.so.1.0.0", 99 "libcrypto.so", 100 ]; 101 } else 102 version(Windows) { 103 enum loadFunction = "LoadLibrary(lib.ptr)"; 104 immutable wstring[] libsslname = [ 105 "libssl32.dll"w, 106 "libssl-1_1"w, 107 "libssl-1_1-x64"w, 108 ]; 109 immutable wstring[] libcryptoname = [ 110 "libeay32.dll"w, 111 "libcrypto-1_1"w, 112 "libcrypto-1_1-x64"w, 113 ]; 114 } else { 115 debug(requests) trace("error loading openssl: unsupported system - first access over https will fail"); 116 return; 117 } 118 119 static if (is(typeof(loadFunction))) { 120 foreach(lib; libsslname) { 121 openssl._libssl = cast(typeof(openssl._libssl))mixin(loadFunction); 122 if ( openssl._libssl !is null ) { 123 debug(requests) tracef("will use %s".format(lib)); 124 break; 125 } 126 } 127 foreach(lib; libcryptoname) { 128 openssl._libcrypto = cast(typeof(openssl._libcrypto))mixin(loadFunction); 129 if ( openssl._libcrypto !is null ) { 130 debug(requests) tracef("will use %s".format(lib)); 131 break; 132 } 133 } 134 } 135 136 if ( openssl._libssl is null ) { 137 debug(requests) trace("warning: failed to load libssl - first access over https will fail"); 138 return; 139 } 140 if ( openssl._libcrypto is null ) { 141 debug(requests) trace("warning: failed to load libcrypto - first access over https will fail"); 142 return; 143 } 144 openssl._ver = openssl.OpenSSL_version_detect(); 145 146 mixin(SSL_Function_set_i!("SSL_library_init", int)); 147 mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_ciphers", void)); 148 mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_digests", void)); 149 mixin(SSL_Function_set_i!("SSL_load_error_strings", void)); 150 151 mixin(SSL_Function_set_i!("OPENSSL_init_ssl", int, ulong, void*)); 152 mixin(CRYPTO_Function_set_i!("OPENSSL_init_crypto", int, ulong, void*)); 153 154 mixin(SSL_Function_set_i!("TLSv1_client_method", SSL_METHOD*)); 155 mixin(SSL_Function_set_i!("TLSv1_2_client_method", SSL_METHOD*)); 156 mixin(SSL_Function_set_i!("TLS_method", SSL_METHOD*)); 157 mixin(SSL_Function_set_i!("SSLv23_client_method", SSL_METHOD*)); 158 mixin(SSL_Function_set_i!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*)); 159 mixin(SSL_Function_set_i!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*)); 160 mixin(SSL_Function_set_i!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*)); 161 mixin(SSL_Function_set_i!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*)); 162 mixin(SSL_Function_set_i!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int)); 163 mixin(SSL_Function_set_i!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int)); 164 mixin(SSL_Function_set_i!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*)); 165 mixin(SSL_Function_set_i!("SSL_CTX_ctrl", long, SSL_CTX*, int, long, void*)); 166 mixin(SSL_Function_set_i!("SSL_new", SSL*, SSL_CTX*)); 167 mixin(SSL_Function_set_i!("SSL_set_fd", int, SSL*, int)); 168 mixin(SSL_Function_set_i!("SSL_connect", int, SSL*)); 169 mixin(SSL_Function_set_i!("SSL_write", int, SSL*, const void*, int)); 170 mixin(SSL_Function_set_i!("SSL_read", int, SSL*, void*, int)); 171 mixin(SSL_Function_set_i!("SSL_free", void, SSL*)); 172 mixin(SSL_Function_set_i!("SSL_CTX_free", void, SSL_CTX*)); 173 mixin(SSL_Function_set_i!("SSL_get_error", int, SSL*, int)); 174 mixin(SSL_Function_set_i!("SSL_ctrl", long, SSL*, int, long, void*)); 175 mixin(CRYPTO_Function_set_i!("ERR_reason_error_string", char*, ulong)); 176 mixin(CRYPTO_Function_set_i!("ERR_get_error", ulong)); 177 178 void delegate()[Version] init_matrix; 179 init_matrix[Version(1,0)] = &openssl.init1_0; 180 init_matrix[Version(1,1)] = &openssl.init1_1; 181 init_matrix[Version(0,2)] = &openssl.init1_1; // libressl >= 2.7.1 182 auto init = init_matrix.get(openssl._ver, null); 183 if ( init is null ) { 184 throw new Exception("loading openssl: unknown version for init"); 185 } 186 init(); 187 } 188 189 struct OpenSSL { 190 191 private { 192 Version _ver; 193 void* _libssl; 194 void* _libcrypto; 195 196 // openssl 1.0.x init functions 197 mixin(SSL_Function_decl!("SSL_library_init", int)); 198 mixin(SSL_Function_decl!("OpenSSL_add_all_ciphers", void)); 199 mixin(SSL_Function_decl!("OpenSSL_add_all_digests", void)); 200 mixin(SSL_Function_decl!("SSL_load_error_strings", void)); 201 202 // openssl 1.1.x init functions 203 mixin(SSL_Function_decl!("OPENSSL_init_ssl", int, ulong, void*)); 204 mixin(SSL_Function_decl!("OPENSSL_init_crypto", int, ulong, void*)); 205 206 // all other functions 207 mixin(SSL_Function_decl!("TLSv1_client_method", SSL_METHOD*)); 208 mixin(SSL_Function_decl!("TLSv1_2_client_method", SSL_METHOD*)); 209 mixin(SSL_Function_decl!("TLS_method", SSL_METHOD*)); 210 mixin(SSL_Function_decl!("SSLv23_client_method", SSL_METHOD*)); 211 mixin(SSL_Function_decl!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*)); 212 mixin(SSL_Function_decl!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*)); 213 mixin(SSL_Function_decl!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*)); 214 mixin(SSL_Function_decl!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*)); 215 mixin(SSL_Function_decl!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int)); 216 mixin(SSL_Function_decl!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int)); 217 mixin(SSL_Function_decl!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*)); 218 mixin(SSL_Function_decl!("SSL_CTX_ctrl", long, SSL_CTX*, int, long, void*)); 219 mixin(SSL_Function_decl!("SSL_new", SSL*, SSL_CTX*)); 220 mixin(SSL_Function_decl!("SSL_set_fd", int, SSL*, int)); 221 mixin(SSL_Function_decl!("SSL_connect", int, SSL*)); 222 mixin(SSL_Function_decl!("SSL_write", int, SSL*, const void*, int)); 223 mixin(SSL_Function_decl!("SSL_read", int, SSL*, void*, int)); 224 mixin(SSL_Function_decl!("SSL_free", void, SSL*)); 225 mixin(SSL_Function_decl!("SSL_CTX_free", void, SSL_CTX*)); 226 mixin(SSL_Function_decl!("SSL_get_error", int, SSL*, int)); 227 mixin(SSL_Function_decl!("SSL_ctrl", long, SSL*, int, long, void*)); 228 mixin(SSL_Function_decl!("ERR_reason_error_string", char*, ulong)); 229 mixin(SSL_Function_decl!("ERR_get_error", ulong)); 230 } 231 232 Version reportVersion() const @nogc nothrow pure { 233 return _ver; 234 }; 235 236 private Version OpenSSL_version_detect() const { 237 ulong function() OpenSSL_version_num = cast(ulong function())DLSYM(cast(void*)_libcrypto, "OpenSSL_version_num".ptr); 238 if ( OpenSSL_version_num ) { 239 auto v = OpenSSL_version_num() & 0xffffffff; 240 return Version((v>>>20) & 0xff, (v>>>28) & 0xff); 241 } 242 return Version(1, 0); 243 } 244 245 private void init1_0() const { 246 adapter_SSL_library_init(); 247 adapter_OpenSSL_add_all_ciphers(); 248 adapter_OpenSSL_add_all_digests(); 249 adapter_SSL_load_error_strings(); 250 } 251 private void init1_1() const { 252 /** 253 Standard initialisation options 254 255 #define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L 256 257 # define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L 258 # define OPENSSL_INIT_ADD_ALL_CIPHERS 0x00000004L 259 # define OPENSSL_INIT_ADD_ALL_DIGESTS 0x00000008L 260 **/ 261 enum OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000L; 262 enum OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002L; 263 enum OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004L; 264 enum OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008L; 265 adapter_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, null); 266 adapter_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, null); 267 } 268 269 SSL_METHOD* TLSv1_client_method() const { 270 if ( adapter_TLSv1_client_method is null ) { 271 throw new Exception("openssl not initialized - is it installed?"); 272 } 273 return adapter_TLSv1_client_method(); 274 } 275 SSL_METHOD* TLSv1_2_client_method() const { 276 if ( adapter_TLSv1_2_client_method is null ) { 277 throw new Exception("openssl not initialized - is it installed?"); 278 } 279 return adapter_TLSv1_2_client_method(); 280 } 281 SSL_METHOD* SSLv23_client_method() const { 282 if ( adapter_SSLv23_client_method is null ) { 283 throw new Exception("can't complete call to SSLv23_client_method"); 284 } 285 return adapter_SSLv23_client_method(); 286 } 287 SSL_METHOD* TLS_method() const { 288 if ( adapter_TLS_method !is null ) { 289 return adapter_TLS_method(); 290 } 291 if ( adapter_SSLv23_client_method !is null ) { 292 return adapter_SSLv23_client_method(); 293 } 294 throw new Exception("can't complete call to TLS_method"); 295 } 296 SSL_CTX* SSL_CTX_new(SSL_METHOD* method) const { 297 if ( adapter_SSL_CTX_new is null ) { 298 throw new Exception("openssl not initialized - is it installed?"); 299 } 300 return adapter_SSL_CTX_new(method); 301 } 302 int SSL_CTX_set_default_verify_paths(SSL_CTX* ctx) const @nogc nothrow { 303 return adapter_SSL_CTX_set_default_verify_paths(ctx); 304 } 305 int SSL_CTX_load_verify_locations(SSL_CTX* ctx, char* CAFile, char* CAPath) const @nogc nothrow { 306 return adapter_SSL_CTX_load_verify_locations(ctx, CAFile, CAPath); 307 } 308 void SSL_CTX_set_verify(SSL_CTX* ctx, int mode, void* callback) const @nogc nothrow { 309 adapter_SSL_CTX_set_verify(ctx, mode, callback); 310 } 311 int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx, const char* file, int type) const @nogc nothrow { 312 return adapter_SSL_CTX_use_PrivateKey_file(ctx, file, type); 313 } 314 int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file, int type) const @nogc nothrow { 315 return adapter_SSL_CTX_use_certificate_file(ctx, file, type); 316 } 317 int SSL_CTX_set_cipher_list(SSL_CTX* ssl_ctx, const char* c) const @nogc nothrow { 318 return adapter_SSL_CTX_set_cipher_list(ssl_ctx, c); 319 } 320 /* 321 * 322 * # define SSL_CTRL_SET_MIN_PROTO_VERSION 123 323 * # define SSL_CTRL_SET_MAX_PROTO_VERSION 124 324 */ 325 enum int SSL_CTRL_SET_MIN_PROTO_VERSION = 123; 326 enum int SSL_CTRL_SET_MAX_PROTO_VERSION = 124; 327 int SSL_CTX_set_min_proto_version(SSL_CTX* ctx, int v) const @nogc nothrow { 328 int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, cast(long)v, null); 329 return r; 330 } 331 int SSL_CTX_set_max_proto_version(SSL_CTX* ctx, int v) const @nogc nothrow { 332 int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, cast(long)v, null); 333 return r; 334 } 335 SSL* SSL_new(SSL_CTX* ctx) const @nogc nothrow { 336 return adapter_SSL_new(ctx); 337 } 338 int SSL_set_fd(SSL* ssl, int fd) const @nogc nothrow { 339 return adapter_SSL_set_fd(ssl, fd); 340 } 341 int SSL_connect(SSL* ssl) const @nogc nothrow { 342 return adapter_SSL_connect(ssl); 343 } 344 int SSL_read(SSL* ssl, void *b, int n) const @nogc nothrow { 345 return adapter_SSL_read(ssl, b, n); 346 } 347 int SSL_write(SSL* ssl, const void *b, int n) const @nogc nothrow { 348 return adapter_SSL_write(ssl, b, n); 349 } 350 void SSL_free(SSL* ssl) const @nogc nothrow @trusted { 351 adapter_SSL_free(ssl); 352 } 353 void SSL_CTX_free(SSL_CTX* ctx) const @nogc nothrow @trusted { 354 adapter_SSL_CTX_free(ctx); 355 } 356 int SSL_get_error(SSL* ssl, int err) const @nogc nothrow { 357 return adapter_SSL_get_error(ssl, err); 358 } 359 long SSL_set_tlsext_host_name(SSL* ssl, const char* host) const @nogc nothrow { 360 enum int SSL_CTRL_SET_TLSEXT_HOSTNAME = 55; 361 enum long TLSEXT_NAMETYPE_host_name = 0; 362 return adapter_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name, cast(void*)host); 363 } 364 char* ERR_reason_error_string(ulong code) const @nogc nothrow { 365 return adapter_ERR_reason_error_string(code); 366 } 367 ulong ERR_get_error() const @nogc nothrow { 368 return adapter_ERR_get_error(); 369 } 370 } 371 /* 372 int main() { 373 import std.socket; 374 375 auto v = openssl.reportVersion(); 376 writefln("openSSL v.%s.%s", v.major, v.minor); 377 openssl.SSL_library_init(); 378 writeln("InitSSL - ok"); 379 SSL_CTX* ctx = openssl.SSL_CTX_new(openssl.TLSv1_client_method()); 380 writefln("SSL_CTX_new = %x", ctx); 381 int r = openssl.adapter_SSL_CTX_set_default_verify_paths(ctx); 382 writefln("SSL_CTX_set_default_verify_paths = %d(%s)", r, r==1?"ok":"fail"); 383 r = openssl.adapter_SSL_CTX_load_verify_locations(ctx, cast(char*)null, cast(char*)null); 384 writefln("SSL_CTX_load_verify_locations - ok"); 385 openssl.SSL_CTX_set_verify(ctx, 0, null); 386 writefln("SSL_CTX_set_verify - ok"); 387 //r = openssl.SSL_CTX_use_PrivateKey_file(ctx, null, 0); 388 //writefln("SSL_CTX_use_PrivateKey_file = %d(%s)", r, r==1?"ok":"fail"); 389 //r = openssl.SSL_CTX_use_certificate_file(ctx, cast(char*), 0); 390 //writefln("SSL_CTX_use_certificate_file = %d(%s)", r, r==1?"ok":"fail"); 391 SSL* ssl = openssl.SSL_new(ctx); 392 writefln("SSL_new = %x", ssl); 393 auto s = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP); 394 Address[] a = getAddress("ns.od.ua", 443); 395 writeln(a[0]); 396 s.connect(a[0]); 397 r = openssl.SSL_set_fd(ssl, s.handle); 398 writefln("SSL_set_fd = %d(%s)", r, r==1?"ok":"fail"); 399 r = openssl.SSL_connect(ssl); 400 writefln("SSL_connect = %d(%s)", r, r==1?"ok":"fail"); 401 if ( r < 0 ) { 402 writefln("code: %d", openssl.SSL_get_error(ssl, r)); 403 } 404 string req = "GET / HTTP/1.0\n\n"; 405 r = openssl.SSL_write(ssl, cast(void*)req.ptr, cast(int)req.length); 406 writefln("SSL_write = %d", r); 407 do { 408 ubyte[] resp = new ubyte[](1024); 409 r = openssl.SSL_read(ssl, cast(void*)resp.ptr, cast(int)1024); 410 writefln("SSL_read = %d", r); 411 if ( r > 0 ) { 412 writeln(cast(string)resp); 413 } 414 } while(r > 0); 415 openssl.SSL_free(ssl); 416 openssl.SSL_CTX_free(ctx); 417 s.close(); 418 return 0; 419 } 420 */