1 /** 2 * OpenSSL support. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.net.ssl.openssl; 15 16 import ae.net.asockets; 17 import ae.net.ssl.ssl; 18 import ae.utils.text; 19 20 import std.conv : to; 21 import std.exception : enforce; 22 import std.functional; 23 import std.socket; 24 import std.string; 25 26 //import deimos.openssl.rand; 27 import deimos.openssl.ssl; 28 import deimos.openssl.err; 29 30 pragma(lib, "ssl"); 31 version(Windows) 32 { pragma(lib, "eay"); } 33 else 34 { pragma(lib, "crypto"); } 35 36 // *************************************************************************** 37 38 shared static this() 39 { 40 SSL_load_error_strings(); 41 SSL_library_init(); 42 } 43 44 // *************************************************************************** 45 46 class SSLContext 47 { 48 SSL_CTX* sslCtx; 49 50 this() 51 { 52 sslCtx = sslEnforce(SSL_CTX_new(SSLv23_client_method())); 53 } 54 } 55 56 SSLContext sslContext; 57 static this() 58 { 59 sslContext = new SSLContext(); 60 sslSocketFactory = toDelegate(&factory); 61 } 62 63 // *************************************************************************** 64 65 class CustomSSLSocket(Parent) : Parent 66 { 67 SSL* sslHandle; 68 69 this(SSLContext context = sslContext) 70 { 71 sslHandle = sslEnforce(SSL_new(context.sslCtx)); 72 SSL_set_connect_state(sslHandle); 73 SSL_set_bio(sslHandle, r.bio, w.bio); 74 } 75 76 MemoryBIO r, w; 77 78 ReadDataHandler userDataHandler; 79 80 override void onReadable() 81 { 82 userDataHandler = handleReadData; 83 handleReadData = &onReadData; 84 scope(exit) handleReadData = userDataHandler; 85 super.onReadable(); 86 } 87 88 void callUserDataHandler(Data data) 89 { 90 assert(handleReadData is &onReadData); 91 scope(exit) 92 if (handleReadData !is &onReadData) 93 { 94 userDataHandler = handleReadData; 95 handleReadData = &onReadData; 96 } 97 userDataHandler(this, data); 98 } 99 100 void onReadData(ClientSocket sender, Data data) 101 { 102 assert(r.data.length == 0, "Would clobber data"); 103 r.set(data.contents); 104 105 if (queue.length) 106 flushQueue(); 107 108 while (r.data.length) 109 { 110 static ubyte[4096] buf; 111 auto result = SSL_read(sslHandle, buf.ptr, buf.length); 112 if (result > 0) 113 callUserDataHandler(Data(buf[0..result])); 114 else 115 { 116 sslError(result); 117 break; 118 } 119 } 120 enforce(r.data.length == 0, "SSL did not consume all read data"); 121 } 122 123 void flushWritten() 124 { 125 if (w.data.length) 126 { 127 super.send([Data(w.data)]); 128 w.clear(); 129 } 130 } 131 132 Data[] queue; 133 134 override void send(Data[] data, int priority = DEFAULT_PRIORITY) 135 { 136 if (data.length) 137 queue ~= data; 138 139 flushQueue(); 140 } 141 142 void flushQueue() 143 { 144 while (queue.length) 145 { 146 auto result = SSL_write(sslHandle, queue[0].ptr, queue[0].length.to!int); 147 if (result > 0) 148 { 149 queue[0] = queue[0][result..$]; 150 if (!queue[0].length) 151 queue = queue[1..$]; 152 } 153 else 154 { 155 sslError(result); 156 break; 157 } 158 } 159 flushWritten(); 160 } 161 162 void sslError(int ret) 163 { 164 auto err = SSL_get_error(sslHandle, ret); 165 switch (err) 166 { 167 case SSL_ERROR_WANT_READ: 168 case SSL_ERROR_ZERO_RETURN: 169 return; 170 default: 171 sslEnforce(false); 172 } 173 } 174 } 175 176 alias CustomSSLSocket!ClientSocket SSLSocket; 177 178 SSLSocket factory() { return new SSLSocket(); } 179 180 // *************************************************************************** 181 182 struct MemoryBIO 183 { 184 @disable this(this); 185 186 this(const(void)[] data) 187 { 188 bio_ = BIO_new_mem_buf(cast(void*)data.ptr, data.length.to!int); 189 } 190 191 void set(const(void)[] data) 192 { 193 BUF_MEM *bptr = BUF_MEM_new(); 194 if (data.length) 195 { 196 BUF_MEM_grow(bptr, data.length); 197 bptr.data[0..bptr.length] = cast(char[])data; 198 } 199 BIO_set_mem_buf(bio, bptr, BIO_CLOSE); 200 } 201 202 void clear() { set(null); } 203 204 @property BIO* bio() 205 { 206 if (!bio_) 207 { 208 bio_ = sslEnforce(BIO_new(BIO_s_mem())); 209 BIO_set_close(bio_, BIO_CLOSE); 210 } 211 return bio_; 212 } 213 214 const(void)[] data() 215 { 216 BUF_MEM *bptr; 217 BIO_get_mem_ptr(bio, &bptr); 218 return bptr.data[0..bptr.length]; 219 } 220 221 private: 222 BIO* bio_; 223 } 224 225 T sslEnforce(T)(T v) 226 { 227 if (v) 228 return v; 229 230 { 231 MemoryBIO m; 232 ERR_print_errors(m.bio); 233 string msg = (cast(char[])m.data).idup; 234 235 throw new Exception(msg); 236 } 237 } 238 239 unittest 240 { 241 // import std.stdio; 242 ClientSocket s = new SSLSocket(); 243 244 s.handleConnect = (ClientSocket c) 245 { 246 // writeln("Connected!"); 247 s.send(Data("GET / HTTP/1.0\r\n\r\n")); 248 }; 249 s.handleReadData = (ClientSocket c, Data data) 250 { 251 // write(cast(string)data.contents); stdout.flush(); 252 }; 253 s.connect("www.openssl.org", 443); 254 socketManager.loop(); 255 }