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 }