1 /**
2 * TLS Blocking API
3 * 
4 * Copyright:
5 * (C) 2013,2015 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.tls.blocking;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_TLS):
15 
16 import botan.tls.client;
17 import botan.tls.server;
18 import botan.rng.rng;
19 import botan.tls.channel;
20 import botan.tls.session_manager;
21 import botan.tls.version_;
22 import botan.utils.mem_ops;
23 import memutils.circularbuffer;
24 import memutils.utils;
25 import std.algorithm;
26 
27 alias DataReader = ubyte[] delegate(ubyte[]);
28 
29 /**
30 * Blocking TLS Channel
31 */
32 struct TLSBlockingChannel
33 {
34 public:
35     @disable this(this);
36     @disable this();
37 
38     /// Client constructor
39     this(DataReader read_fn,
40          DataWriter write_fn,
41 		 OnAlert alert_cb,
42 		 OnHandshakeComplete hs_cb,
43          TLSSessionManager session_manager,
44          TLSCredentialsManager creds,
45          TLSPolicy policy,
46          RandomNumberGenerator rng,
47          in TLSServerInformation server_info = TLSServerInformation(),
48          in TLSProtocolVersion offer_version = TLSProtocolVersion.latestTlsVersion(),
49          Vector!string next_protocols = Vector!string())
50     {
51         m_is_client = true;
52         m_read_fn = read_fn;
53 		m_alert_cb = alert_cb;
54 		m_handshake_complete = hs_cb;
55 		m_readbuf = Vector!ubyte(TLS_DEFAULT_BUFFERSIZE);
56 		scope(failure) m_readbuf.destroy();
57         m_impl.client = new TLSClient(write_fn, &dataCb, &alertCb, &handshakeCb, session_manager, creds,
58             policy, rng, server_info, offer_version, next_protocols.move);
59     }
60 
61     /// Server constructor
62     this(DataReader read_fn,
63          DataWriter write_fn,
64 		 OnAlert alert_cb,
65 		 OnHandshakeComplete hs_cb,
66          TLSSessionManager session_manager,
67          TLSCredentialsManager creds,
68          TLSPolicy policy,
69          RandomNumberGenerator rng,
70          NextProtocolHandler next_proto = null,
71 		 SNIHandler sni_handler = null,
72          bool is_datagram = false,
73          size_t io_buf_sz = 16*1024)
74     {
75         m_is_client = false;
76         m_read_fn = read_fn;
77 		m_alert_cb = alert_cb;
78 		m_handshake_complete = hs_cb;
79 		m_readbuf = Vector!ubyte(TLS_DEFAULT_BUFFERSIZE);
80 		scope(failure) m_readbuf.destroy();
81         m_impl.server = new TLSServer(write_fn, &dataCb, &alertCb, &handshakeCb, session_manager, creds,
82 			policy, rng, next_proto, sni_handler, is_datagram, io_buf_sz);
83     }
84 
85     /**
86     * Blocks until the full handhsake is complete
87     */
88     void doHandshake()
89 	{
90 		assert(!m_slice);
91         
92         while (!m_closed && channel !is null && !channel.isActive())
93         {
94             ubyte[] readref = m_readbuf.ptr[0 .. m_readbuf.length];
95             const ubyte[] from_socket = m_read_fn(readref);
96 			enforce(channel!is null, "Connection closed during handshake");
97             channel.receivedData(cast(const(ubyte)*)from_socket.ptr, from_socket.length);
98         }
99     }
100 
101     /**
102     * Number of bytes pending read in the plaintext buffer (bytes
103     * readable without blocking)
104     */
105 	size_t pending() const { assert(!m_slice); return m_plaintext.length; }
106 
107 	/// Returns an array of pending data
108 	const(ubyte)[] peek() {
109 		assert(!m_slice);
110 		return m_plaintext.length > 0 ? m_plaintext.peek : null;
111 	}
112 
113     /// Reads until the destination ubyte array is full, utilizing internal buffers if necessary
114     void read(ubyte[] dest) 
115     {
116 		ubyte[] destlog = dest;
117 		assert(!m_slice);
118 		//logDebug("remaining length: ", dest.length);
119         ubyte[] remaining = dest;
120         while (remaining.length > 0) {
121             dest = readBuf(remaining);
122             remaining = remaining[dest.length .. $];
123 			//logDebug("remaining length: ", remaining.length);
124         }
125 		//logDebug("finished with: ", cast(string) destlog);
126     }
127 
128     /**
129     * Blocking ( if !pending() ) read, will return at least 1 ubyte or 0 on connection close
130     *  supports replacement of internal read buffer when called until buf.length != returned buffer length
131     */
132 	ubyte[] readBuf(ubyte[] buf)
133     {
134 		assert(!m_slice);
135 		m_reading = true;
136 		scope(exit) m_reading = false;
137 
138 		if (m_plaintext.length != 0) {
139 			size_t len = min(m_plaintext.length, buf.length);
140 			m_plaintext.read(buf[0 .. len]);
141 			return buf[0 .. len];
142 		}
143 		else {
144 	        // we can use our own buffer to optimize the scenarios where the application flushes it instantly
145 	        m_plaintext_override = buf;
146 	        scope(exit) {
147 	            m_slice = null;
148 	            m_plaintext_override = null;
149 	        }
150 		}
151     
152         // if there's nothing in the buffers, read some packets and process them
153         while (!m_slice && m_plaintext.empty && !isClosed)
154         {
155 			ubyte[] slice;
156 			if (m_readbuf.length > 0) {
157 				slice = m_readbuf.ptr[0 .. m_readbuf.length];
158 			}
159 			else break;
160 			const ubyte[] from_socket = m_read_fn(slice);
161 			if (from_socket.length == 0)
162 				break;
163 			enforce(channel !is null, "Connection closed while reading from TLS Channel");
164 			channel.receivedData(cast(const(ubyte)*)from_socket.ptr, from_socket.length);
165 
166 			if (from_socket.length == slice.length && m_readbuf.length < 64*1024) {
167 				size_t next_len = m_readbuf.length * 2;
168 				m_readbuf.destroy();
169 				m_readbuf = Vector!ubyte(next_len);
170 			}
171         }
172 
173 		if (buf.length == 0) return null;
174 
175         // we *should* have something in the override if plaintext/offset is empty
176         if (m_plaintext.length == 0 && m_slice) {
177             buf = m_slice;
178 			//logDebug("Read m_slice: ", buf); 
179             return buf;
180         }
181 
182         assert(!m_slice, "Cannot have both a slice and extensible buffer contents");
183 
184         // unless the override was too small or data was already pending
185         const size_t returned = std.algorithm.min(buf.length, m_plaintext.length);
186 		if (returned == 0) {
187 			//logDebug("Destroyed return object");
188 			return null;
189 		}
190 		m_plaintext.read(buf[0 .. returned]);
191 
192         
193 		//logDebug("Returning data");
194         return buf[0 .. returned];
195     }
196 
197 	void write(in ubyte[] buf) { 
198 		m_writing = true;
199 		scope(exit) m_writing = false;
200 
201 		enforce(channel !is null, "Connection closed when attempting to write to channel"); 
202 		channel.send(cast(const(ubyte)*)buf.ptr, buf.length);
203 	}
204 
205     inout(TLSChannel) underlyingChannel() inout { return channel; }
206 
207 	void close() { enforce(channel); m_closed = true; channel.close(); }
208 
209 	bool isClosed() const { return m_closed || m_impl.client is null; }
210 
211 	@property bool isBusy() const { return m_reading || m_writing; }
212 
213 	const(Vector!X509Certificate) peerCertChain() const { enforce(channel); return channel.peerCertChain(); }
214 
215 	~this()
216 	{
217 		if (isBusy) return;
218 		if (m_is_client)
219 			m_impl.client.destroy(); 
220 		else m_impl.server.destroy();
221 	}
222 
223     /**
224      * get handshake complete notifications
225     */
226     @property void onHandshakeComplete(OnHandshakeComplete handshake_complete)
227     { m_handshake_complete = handshake_complete; }
228 
229     /**
230     * get notification of alerts 
231     */
232     @property void onAlertNotification(OnAlert alert_cb)
233     {
234         m_alert_cb = alert_cb;
235     }
236 
237 private:
238 
239     bool handshakeCb(in TLSSession session)
240     {
241 		//logDebug("Handshake Complete");  
242 		if (m_handshake_complete)
243 	        return m_handshake_complete(session);
244 		return true;
245     }
246 
247     void dataCb(in ubyte[] data)
248     {
249 		//logDebug("Plaintext: ", cast(ubyte[])data);
250         if (m_plaintext.length == 0 && m_plaintext_override && m_slice.length + data.length < m_plaintext_override.length) {
251             m_plaintext_override[m_slice.length .. m_slice.length + data.length] = data[0 .. $];
252             m_slice = m_plaintext_override[0 .. m_slice.length + data.length];
253 			m_plaintext.destroy();
254             return;
255         }
256         else if (m_slice) {
257             // data too large, abandon the override optimization, copy all to the plaintext buffer
258 			m_plaintext.capacity = 8192;
259 			m_plaintext.put(m_slice);
260             m_plaintext_override = null;
261             m_slice = null;
262         }
263 		if (m_plaintext.freeSpace < data.length) {
264 			//logDebug("Growing m_plaintext from: ", m_plaintext.capacity, " to ", 8192 + m_plaintext.length + m_plaintext.freeSpace);
265 			m_plaintext.capacity = 8192 + m_plaintext.length + m_plaintext.freeSpace;
266 		}
267 		m_plaintext.put(data);
268     }
269 
270     void alertCb(in TLSAlert alert, in ubyte[] ub)
271     {
272 		//logDebug("Alert: ", alert.typeString(), " :", ub);  
273 		if (alert.isFatal)
274 			m_closed = true;
275 		if (m_alert_cb)
276 	        m_alert_cb(alert, ub); 
277     }
278 
279     union TLSImpl {
280         TLSClient client;
281         TLSServer server;
282     }
283 
284 	@property inout(TLSChannel) channel() inout { 
285 		return (m_is_client ? cast(inout(TLSChannel)) m_impl.client : cast(inout(TLSChannel)) m_impl.server); 
286 	}
287 
288 	bool m_reading;
289 	bool m_writing;
290     bool m_is_client;
291 	bool m_closed;
292     DataReader m_read_fn;
293     TLSImpl m_impl;
294     OnAlert m_alert_cb;
295     OnHandshakeComplete m_handshake_complete;
296 
297     // Buffer
298     CircularBuffer!(ubyte, 0, SecureMem) m_plaintext;
299 
300     // Buffer optimization
301     ubyte[] m_plaintext_override;
302     ubyte[] m_slice;
303 
304 	Vector!ubyte m_readbuf;
305 }
306