7. hello (TLS)
This server is the first in the series to have some related files. See the fossil repo for, especially, a Makefile to generate the required crypto.
#! /usr/bin/env dub /+ dub.sdl: dependency "openssl" version="~>2.0.3+1.1.0h" dependency "hostname" version="~>0.1.2" libs "ssl" +/ import std.socket : InternetAddress, Socket, TcpSocket, SocketOptionLevel, SocketOption; import std.exception : enforce; import std.stdio : writefln, writeln, stderr; import std.string : toStringz, fromStringz; import deimos.openssl.ssl; import deimos.openssl.err : ERR_print_errors_fp; static import cio = core.stdc.stdio; import hostname : hostnamez; enum port = 4444; void send(ssl_st* ssl, scope string msg) nothrow { SSL_write(ssl, cast(void*) msg.ptr, cast(int) msg.length); } int main() { auto ctx = SSL_CTX_new(TLS_server_method); enforce(ctx, "SSL_CTX_new() failed"); scope (exit) SSL_CTX_free(ctx); if (!SSL_CTX_use_certificate_file(ctx, "ssl.crt", SSL_FILETYPE_PEM) || !SSL_CTX_use_PrivateKey_file(ctx, "ssl.key", SSL_FILETYPE_PEM)) { stderr.writeln("SSL_CTX_use_certificate_file() failed"); ERR_print_errors_fp(cio.stderr); return 1; } scope listener = new TcpSocket; listener.blocking = true; listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 1); listener.bind(new InternetAddress(port)); listener.listen(10); writefln!"Listening on %d."(port); while (true) { writeln; auto client = listener.accept; scope (exit) client.close; writefln!"Received connection from %s."(client.remoteAddress.toString); auto ssl = SSL_new(ctx); enforce(ssl, "SSL_new() failed"); scope (exit) { SSL_shutdown(ssl); SSL_free(ssl); } enforce(SSL_set_tlsext_host_name(ssl, cast(char*) hostnamez), "SSL_set_tlsext_host_name failed"); SSL_set_fd(ssl, client.handle); if (SSL_accept(ssl) <= 0) { stderr.writeln("SSL_accept() failed"); ERR_print_errors_fp(cio.stderr); continue; } writefln!"SSL/TLS using %s"(SSL_get_cipher(ssl).fromStringz); ssl.send("Hello, world!\n"); } assert(0); }
Server interaction
Listening on 4444. Received connection from 127.0.0.1:49434. SSL/TLS using TLS_AES_256_GCM_SHA384
Client interaction
$ ncat --recv-only --ssl localhost 4444 Hello, world! $ ./client.d SSL/TLS using TLS_AES_256_GCM_SHA384 Hello, world! subject: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd issuer: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd Accepting self-signed certificate.
is there a convenient way to get a binary out of this script?
dub can build from these directly:$ dub build --compiler=gdc -brelease --single hello.d Performing "release" build using gdc for x86_64. hello ~master: building configuration "application"...
what is the 'hostname' import about?
That's just a wrapper forgethostname()
, as OpenSSL wants to set a hostname.
when does the crypto kick in?
At theSSL_accept
(in a server), or the SSL_connect
(in a client). After these calls, further networking should use SSL_send
and SSL_read
.
what's with that static import
?
This code uses D's stdio, but also needs to pass a FILE*
to an OpenSSL function for libc stdio. The static import helps to make it clear that I'm passing libc's stderr
where that's passed.