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 enforce(channel !is null, "Connection closed while reading from TLS Channel"); 162 channel.receivedData(cast(const(ubyte)*)from_socket.ptr, from_socket.length); 163 } 164 165 if (buf.length == 0) return null; 166 167 // we *should* have something in the override if plaintext/offset is empty 168 if (m_plaintext.length == 0 && m_slice) { 169 buf = m_slice; 170 //logDebug("Read m_slice: ", buf); 171 return buf; 172 } 173 174 assert(!m_slice, "Cannot have both a slice and extensible buffer contents"); 175 176 // unless the override was too small or data was already pending 177 const size_t returned = std.algorithm.min(buf.length, m_plaintext.length); 178 if (returned == 0) { 179 //logDebug("Destroyed return object"); 180 return null; 181 } 182 m_plaintext.read(buf[0 .. returned]); 183 184 185 //logDebug("Returning data"); 186 return buf[0 .. returned]; 187 } 188 189 void write(in ubyte[] buf) { 190 m_writing = true; 191 scope(exit) m_writing = false; 192 193 enforce(channel !is null, "Connection closed when attempting to write to channel"); 194 channel.send(cast(const(ubyte)*)buf.ptr, buf.length); 195 } 196 197 inout(TLSChannel) underlyingChannel() inout { return channel; } 198 199 void close() { enforce(channel); m_closed = true; channel.close(); } 200 201 bool isClosed() const { return m_closed || m_impl.client is null; } 202 203 @property bool isBusy() const { return m_reading || m_writing; } 204 205 const(Vector!X509Certificate) peerCertChain() const { enforce(channel); return channel.peerCertChain(); } 206 207 ~this() 208 { 209 if (isBusy) return; 210 if (m_is_client) 211 m_impl.client.destroy(); 212 else m_impl.server.destroy(); 213 } 214 215 /** 216 * get handshake complete notifications 217 */ 218 @property void onHandshakeComplete(OnHandshakeComplete handshake_complete) 219 { m_handshake_complete = handshake_complete; } 220 221 /** 222 * get notification of alerts 223 */ 224 @property void onAlertNotification(OnAlert alert_cb) 225 { 226 m_alert_cb = alert_cb; 227 } 228 229 private: 230 231 bool handshakeCb(in TLSSession session) 232 { 233 //logDebug("Handshake Complete"); 234 if (m_handshake_complete) 235 return m_handshake_complete(session); 236 return true; 237 } 238 239 void dataCb(in ubyte[] data) 240 { 241 //logDebug("Plaintext: ", cast(ubyte[])data); 242 if (m_plaintext.length == 0 && m_plaintext_override && m_slice.length + data.length < m_plaintext_override.length) { 243 m_plaintext_override[m_slice.length .. m_slice.length + data.length] = data[0 .. $]; 244 m_slice = m_plaintext_override[0 .. m_slice.length + data.length]; 245 m_plaintext.destroy(); 246 return; 247 } 248 else if (m_slice) { 249 // data too large, abandon the override optimization, copy all to the plaintext buffer 250 m_plaintext.capacity = 8192; 251 m_plaintext.put(m_slice); 252 m_plaintext_override = null; 253 m_slice = null; 254 } 255 if (m_plaintext.freeSpace < data.length) { 256 //logDebug("Growing m_plaintext from: ", m_plaintext.capacity, " to ", 8192 + m_plaintext.length + m_plaintext.freeSpace); 257 m_plaintext.capacity = 8192 + m_plaintext.length + m_plaintext.freeSpace; 258 } 259 m_plaintext.put(data); 260 } 261 262 void alertCb(in TLSAlert alert, in ubyte[] ub) 263 { 264 //logDebug("Alert: ", alert.typeString(), " :", ub); 265 if (alert.isFatal) 266 m_closed = true; 267 if (m_alert_cb) 268 m_alert_cb(alert, ub); 269 } 270 271 union TLSImpl { 272 TLSClient client; 273 TLSServer server; 274 } 275 276 @property inout(TLSChannel) channel() inout { 277 return (m_is_client ? cast(inout(TLSChannel)) m_impl.client : cast(inout(TLSChannel)) m_impl.server); 278 } 279 280 bool m_reading; 281 bool m_writing; 282 bool m_is_client; 283 bool m_closed; 284 DataReader m_read_fn; 285 TLSImpl m_impl; 286 OnAlert m_alert_cb; 287 OnHandshakeComplete m_handshake_complete; 288 289 // Buffer 290 CircularBuffer!(ubyte, 0, SecureMem) m_plaintext; 291 292 // Buffer optimization 293 ubyte[] m_plaintext_override; 294 ubyte[] m_slice; 295 296 Vector!ubyte m_readbuf; 297 } 298