zymosis: cosmetix
[iv.d.git] / sslsocket.d
blob1e0191310a2c0275696d04228280d823e66d0d0c
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // loosely based on opticron and Adam D. Ruppe work
19 module iv.sslsocket /*is aliced*/;
21 import iv.alice;
22 public import std.socket;
23 import iv.gnutls;
26 // ///////////////////////////////////////////////////////////////////////// //
27 shared static this () { gnutls_global_init(); }
28 shared static ~this () { gnutls_global_deinit(); }
31 // ///////////////////////////////////////////////////////////////////////// //
32 /// deprecated!
33 class SSLClientSocket : Socket {
34 gnutls_certificate_credentials_t xcred;
35 gnutls_session_t session;
36 private bool sslInitialized;
37 bool manualHandshake = false; // for non-blocking sockets this should be `true`
39 // take care of pre-connection TLS stuff
40 //FIXME: possible memory leak on exception? (sholdn't be, as `close()` will free the things)
41 private void sslInit () {
42 if (sslInitialized) return;
43 sslInitialized = true;
45 // x509 stuff
46 gnutls_certificate_allocate_credentials(&xcred);
48 // sets the trusted certificate authority file (no need for us, as we aren't checking any certificate)
49 //gnutls_certificate_set_x509_trust_file(xcred, CAFILE, GNUTLS_X509_FMT_PEM);
51 // initialize TLS session
52 gnutls_init(&session, GNUTLS_CLIENT);
54 // use default priorities
55 const(char)* err;
56 auto ret = gnutls_priority_set_direct(session, "PERFORMANCE", &err);
57 if (ret < 0) {
58 import std.string : fromStringz;
59 import std.conv : to;
60 if (ret == GNUTLS_E_INVALID_REQUEST) throw new Exception("Syntax error at: "~err.fromStringz.idup);
61 throw new Exception("TLS Error: returned with "~ret.to!string);
64 // put the x509 credentials to the current session
65 gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
68 public void sslHandshake () {
69 // lob the socket handle off to gnutls
70 gnutls_transport_set_ptr(session, cast(gnutls_transport_ptr_t)handle);
71 // perform the TLS handshake
72 auto ret = gnutls_handshake(session);
73 if (ret < 0) {
74 import std.string : fromStringz;
75 throw new Exception("Handshake failed: "~gnutls_strerror(ret).fromStringz.idup);
79 override void connect (Address to) @trusted {
80 super.connect(to);
81 if (!manualHandshake) sslHandshake();
84 // close the encrypted connection
85 override void close () @trusted {
86 scope(exit) sslInitialized = false;
87 if (sslInitialized) {
88 //{ import core.stdc.stdio : printf; printf("deiniting\n"); }
89 gnutls_bye(session, GNUTLS_SHUT_RDWR);
90 gnutls_deinit(session);
91 gnutls_certificate_free_credentials(xcred);
93 super.close();
96 override ptrdiff_t send (const(void)[] buf, SocketFlags flags) @trusted {
97 return gnutls_record_send(session, buf.ptr, buf.length);
100 override ptrdiff_t send (const(void)[] buf) {
101 import core.sys.posix.sys.socket;
102 static if (is(typeof(MSG_NOSIGNAL))) {
103 return send(buf, cast(SocketFlags)MSG_NOSIGNAL);
104 } else {
105 return send(buf, SocketFlags.NOSIGNAL);
109 override ptrdiff_t receive (void[] buf, SocketFlags flags) @trusted {
110 return gnutls_record_recv(session, buf.ptr, buf.length);
113 override ptrdiff_t receive (void[] buf) { return receive(buf, SocketFlags.NONE); }
115 this (AddressFamily af, SocketType type=SocketType.STREAM) {
116 sslInit();
117 super(af, type);
120 this (socket_t sock, AddressFamily af) {
121 sslInit();
122 super(sock, af);
127 // ///////////////////////////////////////////////////////////////////////// //
128 // this can be used as both client and server socket
129 // don't forget to set certificate file (and key file, if you have both) for server!
130 // `connect()` will do client mode, `accept()` will do server mode (and will return `SSLSocket` instance)
131 class SSLSocket : Socket {
132 gnutls_certificate_credentials_t xcred;
133 gnutls_session_t session;
134 private bool sslInitialized = false;
135 bool manualHandshake = false; // for non-blocking sockets this should be `true`
136 private bool thisIsServer = false;
137 // server
138 private string certfilez; // "cert.pem"
139 private string keyfilez; // "key.pem"
141 // both key and cert can be in one file
142 void setKeyCertFile (const(char)[] certname, const(char)[] keyname=null) {
143 if (certname.length == 0) { certname = keyname; keyname = null; }
144 if (certname.length == 0) {
145 certfilez = keyfilez = "";
146 } else {
147 auto buf = new char[](certname.length+1);
148 buf[] = 0;
149 buf[0..certname.length] = certname;
150 certfilez = cast(string)buf;
151 if (keyname.length != 0) {
152 buf = new char[](keyname.length+1);
153 buf[] = 0;
154 buf[0..keyname.length] = keyname;
156 keyfilez = cast(string)buf;
160 // take care of pre-connection TLS stuff
161 //FIXME: possible memory leak on exception? (sholdn't be, as `close()` will free the things)
162 private void sslInit () {
163 if (sslInitialized) return;
164 sslInitialized = true;
166 // x509 stuff
167 gnutls_certificate_allocate_credentials(&xcred);
169 // sets the trusted certificate authority file (no need for us, as we aren't checking any certificate)
170 //gnutls_certificate_set_x509_trust_file(xcred, CAFILE, GNUTLS_X509_FMT_PEM);
172 if (thisIsServer) {
173 // server
174 if (certfilez.length < 1) throw new SocketException("TLS Error: certificate file not set");
175 if (keyfilez.length < 1) throw new SocketException("TLS Error: key file not set");
176 auto res = gnutls_certificate_set_x509_key_file(xcred, certfilez.ptr, keyfilez.ptr, GNUTLS_X509_FMT_PEM);
177 if (res < 0) {
178 import std.conv : to;
179 throw new SocketException("TLS Error: returned with "~res.to!string);
181 gnutls_init(&session, GNUTLS_SERVER);
182 gnutls_certificate_server_set_request(session, GNUTLS_CERT_IGNORE);
183 gnutls_handshake_set_timeout(session, /*GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT*/2300);
184 } else {
185 // client
186 // initialize TLS session
187 gnutls_init(&session, GNUTLS_CLIENT);
190 // use default priorities
191 const(char)* err;
192 auto ret = gnutls_priority_set_direct(session, "PERFORMANCE", &err);
193 if (ret < 0) {
194 import std.string : fromStringz;
195 import std.conv : to;
196 if (ret == GNUTLS_E_INVALID_REQUEST) throw new SocketException("Syntax error at: "~err.fromStringz.idup);
197 throw new SocketException("TLS Error: returned with "~ret.to!string);
200 // put the x509 credentials to the current session
201 gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
204 public void sslHandshake () {
205 sslInit();
206 // lob the socket handle off to gnutls
207 gnutls_transport_set_ptr(session, cast(gnutls_transport_ptr_t)handle);
208 // perform the TLS handshake
209 auto ret = gnutls_handshake(session);
210 if (ret < 0) {
211 import std.string : fromStringz;
212 throw new Exception("Handshake failed: "~gnutls_strerror(ret).fromStringz.idup);
216 override @property void blocking (bool byes) @trusted {
217 super.blocking(byes);
218 manualHandshake = !byes;
221 override void connect (Address to) @trusted {
222 if (sslInitialized && thisIsServer) throw new SocketException("wtf?!");
223 thisIsServer = false;
224 sslInit();
225 super.connect(to);
226 if (!manualHandshake) sslHandshake();
229 protected override Socket accepting () pure nothrow {
230 return new SSLSocket();
233 override Socket accept () @trusted {
234 auto sk = super.accept();
235 if (auto ssk = cast(SSLSocket)sk) {
236 ssk.keyfilez = keyfilez;
237 ssk.certfilez = certfilez;
238 ssk.manualHandshake = manualHandshake;
239 ssk.thisIsServer = true;
240 ssk.sslInit();
241 if (!ssk.manualHandshake) ssk.sslHandshake();
242 } else {
243 throw new SocketAcceptException("failed to create ssl socket");
245 return sk;
248 // close the encrypted connection
249 override void close () @trusted {
250 scope(exit) sslInitialized = false;
251 if (sslInitialized) {
252 //{ import core.stdc.stdio : printf; printf("deiniting\n"); }
253 gnutls_bye(session, GNUTLS_SHUT_RDWR);
254 gnutls_deinit(session);
255 gnutls_certificate_free_credentials(xcred);
257 super.close();
260 override ptrdiff_t send (const(void)[] buf, SocketFlags flags) @trusted {
261 if (session is null || !sslInitialized) throw new SocketException("not initialized");
262 return gnutls_record_send(session, buf.ptr, buf.length);
265 override ptrdiff_t send (const(void)[] buf) {
266 import core.sys.posix.sys.socket;
267 static if (is(typeof(MSG_NOSIGNAL))) {
268 return send(buf, cast(SocketFlags)MSG_NOSIGNAL);
269 } else {
270 return send(buf, SocketFlags.NOSIGNAL);
274 override ptrdiff_t receive (void[] buf, SocketFlags flags) @trusted {
275 if (session is null || !sslInitialized) throw new SocketException("not initialized");
276 return gnutls_record_recv(session, buf.ptr, buf.length);
279 override ptrdiff_t receive (void[] buf) { return receive(buf, SocketFlags.NONE); }
281 private this () pure nothrow @safe {}
283 this (AddressFamily af, SocketType type=SocketType.STREAM) {
284 super(af, type);
287 this (socket_t sock, AddressFamily af) {
288 super(sock, af);