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