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.