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 string SSL_Function_decl(string N, R, A...)() {
31     string F = "extern (C) %s function %s adapter_%s;".format(R.stringof, A.stringof, N);
32     return F;
33 }
34 string SSL_Function_set_i(string N, R, A...)() {
35     string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libssl, \"%s\");".format(N, N, N);
36     return F;
37 }
38 string CRYPTO_Function_set_i(string N, R, A...)() {
39     string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libcrypto, \"%s\");".format(N, N, N);
40     return F;
41 }
42 
43 private alias Version = Tuple!(int, "major", int, "minor");
44 
45 immutable static OpenSSL openssl;
46 
47 static this() {
48     if ( openssl._libssl !is null ) {
49         return;
50     }
51     version(OSX) {
52         openssl._libssl = cast(typeof(openssl._libssl))dlopen("libssl.dylib", RTLD_LAZY);
53         openssl._libcrypto = cast(typeof(openssl._libcrypto))dlopen("libcrypto.dylib", RTLD_LAZY);
54     } else
55     version(linux) {
56         openssl._libssl = cast(typeof(openssl._libssl))dlopen("libssl.so", RTLD_LAZY);
57         openssl._libcrypto = cast(typeof(openssl._libcrypto))dlopen("libcrypto.so", RTLD_LAZY);
58     } else
59     version(Windows) {
60         openssl._libssl = cast(typeof(openssl._libssl))LoadLibrary("libssl32.dll");
61         openssl._libcrypto = cast(typeof(openssl._libcrypto))LoadLibrary("libeay32.dll");
62     } else {
63         throw new Exception("loading openssl: unsupported system");
64     }
65     if ( openssl._libssl is null ) {
66         error("warning: failed to load libssl - first access over https will fail");
67         return;
68     }
69     if ( openssl._libcrypto is null ) {
70         error("warning: failed to load libcrypto - first access over https will fail");
71         return;
72     }
73     openssl._ver = openssl.OpenSSL_version_detect();
74 
75     mixin(SSL_Function_set_i!("SSL_library_init", int));
76     mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_ciphers", void));
77     mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_digests", void));
78     mixin(SSL_Function_set_i!("SSL_load_error_strings", void));
79 
80     mixin(SSL_Function_set_i!("OPENSSL_init_ssl", int, ulong, void*));
81     mixin(CRYPTO_Function_set_i!("OPENSSL_init_crypto", int, ulong, void*));
82 
83     mixin(SSL_Function_set_i!("TLSv1_client_method", SSL_METHOD*));
84     mixin(SSL_Function_set_i!("TLSv1_2_client_method", SSL_METHOD*));
85     mixin(SSL_Function_set_i!("TLS_method", SSL_METHOD*));
86     mixin(SSL_Function_set_i!("SSLv23_client_method", SSL_METHOD*));
87     mixin(SSL_Function_set_i!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*));
88     mixin(SSL_Function_set_i!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*));
89     mixin(SSL_Function_set_i!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*));
90     mixin(SSL_Function_set_i!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*));
91     mixin(SSL_Function_set_i!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int));
92     mixin(SSL_Function_set_i!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int));
93     mixin(SSL_Function_set_i!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*));
94     mixin(SSL_Function_set_i!("SSL_CTX_ctrl", long, SSL_CTX*, int, long, void*));
95     mixin(SSL_Function_set_i!("SSL_new", SSL*, SSL_CTX*));
96     mixin(SSL_Function_set_i!("SSL_set_fd", int, SSL*, int));
97     mixin(SSL_Function_set_i!("SSL_connect", int, SSL*));
98     mixin(SSL_Function_set_i!("SSL_write", int, SSL*, const void*, int));
99     mixin(SSL_Function_set_i!("SSL_read", int, SSL*, void*, int));
100     mixin(SSL_Function_set_i!("SSL_free", void, SSL*));
101     mixin(SSL_Function_set_i!("SSL_CTX_free", void, SSL_CTX*));
102     mixin(SSL_Function_set_i!("SSL_get_error", int, SSL*, int));
103     mixin(SSL_Function_set_i!("SSL_ctrl", long, SSL*, int, long, void*));
104     mixin(SSL_Function_set_i!("ERR_reason_error_string", char*, ulong));
105     mixin(SSL_Function_set_i!("ERR_get_error", ulong));
106 
107     void delegate()[Version] init_matrix;
108     init_matrix[Version(1,0)] = &openssl.init1_0;
109     init_matrix[Version(1,1)] = &openssl.init1_1;
110     auto init = init_matrix.get(openssl._ver, null);
111     if ( init is null ) {
112         throw new Exception("loading openssl: unknown version for init");
113     }
114     init();
115 }
116 
117 struct OpenSSL {
118 
119     private {
120         Version         _ver;
121         void*           _libssl;
122         void*           _libcrypto;
123 
124         // openssl 1.0.x init functions
125         mixin(SSL_Function_decl!("SSL_library_init", int));
126         mixin(SSL_Function_decl!("OpenSSL_add_all_ciphers", void));
127         mixin(SSL_Function_decl!("OpenSSL_add_all_digests", void));
128         mixin(SSL_Function_decl!("SSL_load_error_strings", void));
129 
130         // openssl 1.1.x init functions
131         mixin(SSL_Function_decl!("OPENSSL_init_ssl", int, ulong, void*));
132         mixin(SSL_Function_decl!("OPENSSL_init_crypto", int, ulong, void*));
133 
134         // all other functions
135         mixin(SSL_Function_decl!("TLSv1_client_method", SSL_METHOD*));
136         mixin(SSL_Function_decl!("TLSv1_2_client_method", SSL_METHOD*));
137         mixin(SSL_Function_decl!("TLS_method", SSL_METHOD*));
138         mixin(SSL_Function_decl!("SSLv23_client_method", SSL_METHOD*));
139         mixin(SSL_Function_decl!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*));
140         mixin(SSL_Function_decl!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*));
141         mixin(SSL_Function_decl!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*));
142         mixin(SSL_Function_decl!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*));
143         mixin(SSL_Function_decl!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int));
144         mixin(SSL_Function_decl!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int));
145         mixin(SSL_Function_decl!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*));
146         mixin(SSL_Function_decl!("SSL_CTX_ctrl", long, SSL_CTX*, int, long, void*));
147         mixin(SSL_Function_decl!("SSL_new", SSL*, SSL_CTX*));
148         mixin(SSL_Function_decl!("SSL_set_fd", int, SSL*, int));
149         mixin(SSL_Function_decl!("SSL_connect", int, SSL*));
150         mixin(SSL_Function_decl!("SSL_write", int, SSL*, const void*, int));
151         mixin(SSL_Function_decl!("SSL_read", int, SSL*, void*, int));
152         mixin(SSL_Function_decl!("SSL_free", void, SSL*));
153         mixin(SSL_Function_decl!("SSL_CTX_free", void, SSL_CTX*));
154         mixin(SSL_Function_decl!("SSL_get_error", int, SSL*, int));
155         mixin(SSL_Function_decl!("SSL_ctrl", long, SSL*, int, long, void*));
156         mixin(SSL_Function_decl!("ERR_reason_error_string", char*, ulong));
157         mixin(SSL_Function_decl!("ERR_get_error", ulong));
158     }
159 
160     Version reportVersion() const @nogc nothrow pure {
161         return _ver;
162     };
163 
164     private Version OpenSSL_version_detect() const {
165         ulong function() OpenSSL_version_num = cast(ulong function())DLSYM(cast(void*)_libcrypto, "OpenSSL_version_num".ptr);
166         if ( OpenSSL_version_num ) {
167             auto v = OpenSSL_version_num() & 0xffffffff;
168             return Version((v>>>20) & 0xff, (v>>>28) & 0xff);
169         }
170         return Version(1, 0);
171     }
172 
173     private void init1_0() const {
174         adapter_SSL_library_init();
175         adapter_OpenSSL_add_all_ciphers();
176         adapter_OpenSSL_add_all_digests();
177         adapter_SSL_load_error_strings();
178     }
179     private void init1_1() const {
180         /**
181         Standard initialisation options
182 
183         #define OPENSSL_INIT_LOAD_SSL_STRINGS       0x00200000L
184 
185         # define OPENSSL_INIT_LOAD_CRYPTO_STRINGS    0x00000002L
186         # define OPENSSL_INIT_ADD_ALL_CIPHERS        0x00000004L
187         # define OPENSSL_INIT_ADD_ALL_DIGESTS        0x00000008L
188         **/
189         enum OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000L;
190         enum OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002L;
191         enum OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004L;
192         enum OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008L;
193         adapter_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, null);
194         adapter_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, null);
195     }
196 
197     SSL_METHOD* TLSv1_client_method() const {
198         if ( adapter_TLSv1_client_method is null ) {
199             throw new Exception("openssl not initialized - is it installed?");
200         }
201         return adapter_TLSv1_client_method();
202     }
203     SSL_METHOD* TLSv1_2_client_method() const {
204         if ( adapter_TLSv1_2_client_method is null ) {
205             throw new Exception("openssl not initialized - is it installed?");
206         }
207         return adapter_TLSv1_2_client_method();
208     }
209     SSL_METHOD* SSLv23_client_method() const {
210         if ( adapter_SSLv23_client_method is null ) {
211             throw new Exception("can't complete call to SSLv23_client_method");
212         }
213         return adapter_SSLv23_client_method();
214     }
215     SSL_METHOD* TLS_method() const {
216         if ( adapter_TLS_method !is null ) {
217             return adapter_TLS_method();
218         }
219         if ( adapter_SSLv23_client_method !is null ) {
220             return adapter_SSLv23_client_method();
221         }
222         throw new Exception("can't complete call to TLS_method");
223     }
224     SSL_CTX* SSL_CTX_new(SSL_METHOD* method) const {
225         if ( adapter_SSL_CTX_new is null ) {
226             throw new Exception("openssl not initialized - is it installed?");
227         }
228         return adapter_SSL_CTX_new(method);
229     }
230     int SSL_CTX_set_default_verify_paths(SSL_CTX* ctx) const {
231         return adapter_SSL_CTX_set_default_verify_paths(ctx);
232     }
233     int SSL_CTX_load_verify_locations(SSL_CTX* ctx, char* CAFile, char* CAPath) const {
234         return adapter_SSL_CTX_load_verify_locations(ctx, CAFile, CAPath);
235     }
236     void SSL_CTX_set_verify(SSL_CTX* ctx, int mode, void* callback) const {
237         adapter_SSL_CTX_set_verify(ctx, mode, callback);
238     }
239     int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx, const char* file, int type) const {
240         return adapter_SSL_CTX_use_PrivateKey_file(ctx, file, type);
241     }
242     int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file, int type) const {
243         return adapter_SSL_CTX_use_certificate_file(ctx, file, type);
244     }
245     int SSL_CTX_set_cipher_list(SSL_CTX* ssl_ctx, const char* c) const {
246         return adapter_SSL_CTX_set_cipher_list(ssl_ctx, c);
247     }
248     /*
249      *
250      * # define SSL_CTRL_SET_MIN_PROTO_VERSION          123
251      * # define SSL_CTRL_SET_MAX_PROTO_VERSION          124
252     */
253     enum int SSL_CTRL_SET_MIN_PROTO_VERSION = 123;
254     enum int SSL_CTRL_SET_MAX_PROTO_VERSION = 124;
255     int SSL_CTX_set_min_proto_version(SSL_CTX* ctx, int v) const {
256         int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, cast(long)v, null);
257         return r;
258     }
259     int SSL_CTX_set_max_proto_version(SSL_CTX* ctx, int v) const {
260         int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, cast(long)v, null);
261         return r;
262     }
263     SSL* SSL_new(SSL_CTX* ctx) const {
264         return adapter_SSL_new(ctx);
265     }
266     int SSL_set_fd(SSL* ssl, int fd) const {
267         return adapter_SSL_set_fd(ssl, fd);
268     }
269     int SSL_connect(SSL* ssl) const {
270         return adapter_SSL_connect(ssl);
271     }
272     int SSL_read(SSL* ssl, void *b, int n) const {
273         return adapter_SSL_read(ssl, b, n);
274     }
275     int SSL_write(SSL* ssl, const void *b, int n) const {
276         return adapter_SSL_write(ssl, b, n);
277     }
278     void SSL_free(SSL* ssl) const {
279         adapter_SSL_free(ssl);
280     }
281     void SSL_CTX_free(SSL_CTX* ctx) const {
282         adapter_SSL_CTX_free(ctx);
283     }
284     int SSL_get_error(SSL* ssl, int err) const {
285         return adapter_SSL_get_error(ssl, err);
286     }
287     long SSL_set_tlsext_host_name(SSL* ssl, const char* host) const {
288         enum int SSL_CTRL_SET_TLSEXT_HOSTNAME = 55;
289         enum long TLSEXT_NAMETYPE_host_name = 0;
290         return adapter_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name, cast(void*)host);
291     }
292     char* ERR_reason_error_string(ulong code) const {
293         return adapter_ERR_reason_error_string(code);
294     }
295     ulong ERR_get_error() const {
296         return adapter_ERR_get_error();
297     }
298 }
299 /*
300 int main() {
301     import std.socket;
302 
303     auto v = openssl.reportVersion();
304     writefln("openSSL v.%s.%s", v.major, v.minor);
305     openssl.SSL_library_init();
306     writeln("InitSSL - ok");
307     SSL_CTX* ctx = openssl.SSL_CTX_new(openssl.TLSv1_client_method());
308     writefln("SSL_CTX_new = %x", ctx);
309     int r = openssl.adapter_SSL_CTX_set_default_verify_paths(ctx);
310     writefln("SSL_CTX_set_default_verify_paths = %d(%s)", r, r==1?"ok":"fail");
311     r = openssl.adapter_SSL_CTX_load_verify_locations(ctx, cast(char*)null, cast(char*)null);
312     writefln("SSL_CTX_load_verify_locations - ok");
313     openssl.SSL_CTX_set_verify(ctx, 0, null);
314     writefln("SSL_CTX_set_verify - ok");
315     //r = openssl.SSL_CTX_use_PrivateKey_file(ctx, null, 0);
316     //writefln("SSL_CTX_use_PrivateKey_file = %d(%s)", r, r==1?"ok":"fail");
317     //r = openssl.SSL_CTX_use_certificate_file(ctx, cast(char*), 0);
318     //writefln("SSL_CTX_use_certificate_file = %d(%s)", r, r==1?"ok":"fail");
319     SSL* ssl = openssl.SSL_new(ctx);
320     writefln("SSL_new = %x", ssl);
321     auto s = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
322     Address[] a = getAddress("ns.od.ua", 443);
323     writeln(a[0]);
324     s.connect(a[0]);
325     r = openssl.SSL_set_fd(ssl, s.handle);
326     writefln("SSL_set_fd = %d(%s)", r, r==1?"ok":"fail");
327     r = openssl.SSL_connect(ssl);
328     writefln("SSL_connect = %d(%s)", r, r==1?"ok":"fail");
329     if ( r < 0 ) {
330         writefln("code: %d", openssl.SSL_get_error(ssl, r));
331     }
332     string req = "GET / HTTP/1.0\n\n";
333     r = openssl.SSL_write(ssl, cast(void*)req.ptr, cast(int)req.length);
334     writefln("SSL_write = %d", r);
335     do {
336         ubyte[]  resp = new ubyte[](1024);
337         r = openssl.SSL_read(ssl, cast(void*)resp.ptr, cast(int)1024);
338         writefln("SSL_read = %d", r);
339         if ( r > 0 ) {
340             writeln(cast(string)resp);
341         }
342     } while(r > 0);
343     openssl.SSL_free(ssl);
344     openssl.SSL_CTX_free(ctx);
345     s.close();
346     return 0;
347 }
348 */