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*/;
22 public import std
.socket
;
26 // ///////////////////////////////////////////////////////////////////////// //
27 shared static this () { gnutls_global_init(); }
28 shared static ~this () { gnutls_global_deinit(); }
31 // ///////////////////////////////////////////////////////////////////////// //
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;
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
56 auto ret = gnutls_priority_set_direct(session
, "PERFORMANCE", &err
);
58 import std
.string
: fromStringz
;
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
);
74 import std
.string
: fromStringz
;
75 throw new Exception("Handshake failed: "~gnutls_strerror(ret).fromStringz
.idup
);
79 override void connect (Address to
) @trusted {
81 if (!manualHandshake
) sslHandshake();
84 // close the encrypted connection
85 override void close () @trusted {
86 scope(exit
) sslInitialized
= false;
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
);
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
);
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
) {
120 this (socket_t sock
, AddressFamily 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;
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
= "";
147 auto buf
= new char[](certname
.length
+1);
149 buf
[0..certname
.length
] = certname
;
150 certfilez
= cast(string
)buf
;
151 if (keyname
.length
!= 0) {
152 buf
= new char[](keyname
.length
+1);
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;
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);
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
);
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);
186 // initialize TLS session
187 gnutls_init(&session
, GNUTLS_CLIENT
);
190 // use default priorities
192 auto ret = gnutls_priority_set_direct(session
, "PERFORMANCE", &err
);
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 () {
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
);
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;
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;
241 if (!ssk
.manualHandshake
) ssk
.sslHandshake();
243 throw new SocketAcceptException("failed to create ssl socket");
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
);
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
);
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
) {
287 this (socket_t sock
, AddressFamily af
) {