1 module mail.socket; 2 3 import std.conv : to; 4 import std.socket; 5 6 import deimos.openssl.conf; 7 import deimos.openssl.err; 8 import deimos.openssl.ssl; 9 10 enum EncryptionMethod : uint 11 { 12 None, // No encryption is used 13 // SSLv23, // SSL version 3 but rollback to 2 14 // SSLv3, // SSL version 3 encryption 15 TLSv1, // TLS version 1 encryption 16 TLSv1_1, // TLS version 1.1 encryption 17 TLSv1_2, // TLS version 1.2 encryption 18 } 19 20 class Socket 21 { 22 private 23 { 24 bool _open; 25 string _host; 26 ushort _port; 27 TcpSocket _sock; 28 char[4096] _buff; 29 30 bool _secure; 31 bool _verified; 32 33 SSL_METHOD* _sslMethod; 34 SSL_CTX* _sslCtx; 35 SSL* _ssl; 36 X509* _x509; 37 } 38 39 public: 40 @property bool isOpen() const 41 { 42 return _open; 43 } 44 45 @property bool isSecure() const 46 { 47 return _secure; 48 } 49 50 @property bool isCertVerified() const 51 { 52 return _verified; 53 } 54 55 @property string hostName() const 56 { 57 return _sock.hostName; 58 } 59 60 public: 61 this(string host, ushort port) 62 { 63 _host = host; 64 _port = port; 65 } 66 67 ~this() 68 { 69 SSLEnd(); 70 } 71 72 bool connect() 73 { 74 try 75 { 76 auto ai = getAddress(_host, _port); 77 if (ai.length) 78 { 79 if (_sock !is null) 80 { 81 delete _sock; 82 _sock = null; 83 } 84 _sock = new TcpSocket(ai[0].addressFamily); 85 _sock.connect(ai[0]); 86 return _open = true; 87 } 88 } 89 catch (Throwable) {} 90 return false; 91 } 92 93 void disconnect() 94 { 95 if (_sock !is null) 96 { 97 _open = false; 98 _sock.shutdown(SocketShutdown.BOTH); 99 _sock.close(); 100 } 101 } 102 103 bool SSLbegin(EncryptionMethod encMethod = EncryptionMethod.TLSv1_2) 104 { 105 import std.stdio; 106 107 if (_sock is null) 108 { 109 return false; 110 } 111 112 // Init 113 OPENSSL_config(""); 114 SSL_library_init(); 115 SSL_load_error_strings(); 116 117 final switch (encMethod) 118 { 119 // case EncryptionMethod.SSLv23: 120 // _sslMethod = cast(SSL_METHOD*) SSLv23_client_method(); 121 // break; 122 // case EncryptionMethod.SSLv3: 123 // _sslMethod = cast(SSL_METHOD*) SSLv3_client_method(); 124 // break; 125 case EncryptionMethod.TLSv1: 126 _sslMethod = cast(SSL_METHOD*) TLSv1_client_method(); 127 break; 128 case EncryptionMethod.TLSv1_1: 129 _sslMethod = cast(SSL_METHOD*) TLSv1_2_client_method(); 130 break; 131 case EncryptionMethod.TLSv1_2: 132 _sslMethod = cast(SSL_METHOD*) TLSv1_2_client_method(); 133 break; 134 case EncryptionMethod.None: 135 return false; 136 } 137 138 _sslCtx = SSL_CTX_new(cast(const(SSL_METHOD*))(_sslMethod)); 139 if (_sslCtx is null) 140 return false; 141 142 // Stream 143 _ssl = SSL_new(_sslCtx); 144 if (_ssl is null) 145 return false; 146 147 version (Win64) 148 SSL_set_fd(_ssl, cast(int) _sock.handle); 149 else 150 SSL_set_fd(_ssl, _sock.handle); 151 152 // Handshake 153 if (SSL_connect(_ssl) != 1) 154 return false; 155 156 _x509 = SSL_get_peer_certificate(_ssl); 157 158 if (_x509 is null) 159 return false; 160 161 _secure = true; 162 163 // Verify 164 if (SSL_get_verify_result(_ssl) != X509_V_OK) 165 { 166 _verified = false; 167 } 168 else 169 { 170 _verified = true; 171 } 172 return _secure; 173 } 174 175 void SSLEnd() 176 { 177 if (_secure) 178 { 179 _secure = false; 180 SSL_shutdown(_ssl); 181 } 182 183 if (_x509 !is null) 184 { 185 X509_free(_x509); 186 _x509 = null; 187 } 188 189 if (_ssl !is null) 190 { 191 SSL_free(_ssl); 192 _ssl = null; 193 } 194 195 if (_sslCtx !is null) 196 { 197 SSL_CTX_free(_sslCtx); 198 _sslCtx = null; 199 } 200 } 201 202 bool send(string data) 203 { 204 if (_sock is null) 205 { 206 return false; 207 } 208 209 if (_secure) 210 { 211 return SSL_write(_ssl, data.ptr, data.length.to!int) >= 0; 212 } 213 return _sock.send(data) == data.length; 214 } 215 216 string receive() 217 { 218 int len; 219 if (_sock is null) 220 { 221 // throw exception? 222 return ""; 223 } 224 225 if (_secure) 226 { 227 len = SSL_read(_ssl, _buff.ptr, _buff.length); 228 if (len < 0) 229 { 230 len = 0; 231 } 232 } 233 else 234 { 235 len = cast(int) _sock.receive(_buff); 236 } 237 return _buff[0 .. len].to!string; 238 } 239 }