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 }