Skip to content

Commit fbd3b63

Browse files
committed
Auto-detect OpenSSL version
This script was originally written for vibe-d:tls module. However, it makes more sense to have the detection done in bindings. Before this commit, the OpenSSL version was just assumed to be 1.1.0h.
1 parent 563c4b2 commit fbd3b63

File tree

4 files changed

+224
-16
lines changed

4 files changed

+224
-16
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Generated version file
2+
/source/deimos/openssl/version_.d
3+
4+
# DUB artifacts
5+
/.dub/
6+
/openssl
7+
/*-test-library

dub.sdl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ homepage "http://www.openssl.org/"
44
license "OpenSSL or SSLeay"
55
libs "ssl" "crypto" platform="posix"
66

7-
configuration "library" {
7+
configuration "library-autodetect" {
8+
targetType "sourceLibrary"
9+
excludedSourceFiles "source/deimos/openssl/applink.d"
10+
preGenerateCommands `${DUB} scripts/generate_version.d` platform="posix"
11+
versions `DeimosOpenSSLAutoDetect`
12+
}
13+
14+
configuration "library-manual-version" {
815
targetType "sourceLibrary"
916
excludedSourceFiles "source/deimos/openssl/applink.d"
1017
}
@@ -19,4 +26,6 @@ configuration "unittest" {
1926
targetType "executable"
2027
dflags "-main"
2128
excludedSourceFiles "source/deimos/openssl/applink.d"
29+
preGenerateCommands `${DUB} scripts/generate_version.d` platform="posix"
30+
versions `DeimosOpenSSLAutoDetect`
2231
}

scripts/generate_version.d

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/+ dub.sdl:
2+
name "script"
3+
+/
4+
5+
/**
6+
* This program will attempt to detect which version of openssl is installed
7+
*
8+
* End-users might have different versions of OpenSSL installed.
9+
* The version might ever differ among members of a development team.
10+
*
11+
* This script attempts to first calls `pkg-config` to find out the version,
12+
* then reverts to calling the `openssl` binary if `pkg-config` didn't work.
13+
*
14+
* It is called directly as a `preGenerateCommand` (see dub.sdl).
15+
* To use it with another build system, pass the directory in which to write
16+
* the `version_.d` file as first and only argument. The directory
17+
* must exist, this script will not create it.
18+
*/
19+
module generate_version;
20+
21+
import std.algorithm;
22+
import std.conv;
23+
import std.file;
24+
import std.functional;
25+
import std.path;
26+
import std.process;
27+
import std.range;
28+
import std.stdio;
29+
import std.string;
30+
import std.uni;
31+
32+
// file full path is: $SOME_PATH/openssl/scripts/generate_version.d
33+
// We want: $SOME_PATH/openssl/deimos/openssl/
34+
immutable TARGET_DIR_PATH = __FILE_FULL_PATH__
35+
.dirName.dirName.buildPath("source", "deimos", "openssl");
36+
37+
void main(string[] args)
38+
{
39+
string target;
40+
41+
if (args.length == 2)
42+
{
43+
assert(args[1].isDir(),
44+
"OpenSSL version detection: Argument '" ~ args[1] ~ "' is not a directory");
45+
target = args[1].buildPath("version_.d");
46+
}
47+
else
48+
{
49+
assert(args.length == 1,
50+
"OpenSSL version detection expects only one argument, " ~
51+
"a directory path where to write `version_.d`");
52+
target = TARGET_DIR_PATH.buildPath("version_.d");
53+
}
54+
55+
string opensslVersion;
56+
try
57+
{
58+
const res = execute(["pkg-config", "openssl", "--modversion"]);
59+
if (res.status == 0)
60+
opensslVersion = res.output.strip();
61+
}
62+
catch (Exception e) {}
63+
64+
if (!opensslVersion.length) try
65+
{
66+
const res = execute(["openssl", "version"]).output;
67+
if (res.canFind("OpenSSL "))
68+
{
69+
opensslVersion = res.splitter(" ").dropOne.front.filter!(not!(std.uni.isAlpha)).text;
70+
}
71+
else if (res.canFind("LibreSSL "))
72+
{
73+
writeln("\tWarning: Your default openssl binary points to LibreSSL, which is not supported.");
74+
version (OSX)
75+
{
76+
writeln("\tOn Mac OSX, this is the default behavior.");
77+
writeln("\tIf you installed openssl via a package manager, you need to tell DUB how to find it.");
78+
writeln("\tAssuming brew, run [brew link openssl] and follow the instructions for pkg-config.\n");
79+
}
80+
}
81+
}
82+
catch (Exception e) {}
83+
84+
if (!opensslVersion.length)
85+
{
86+
writeln("\tWarning: Could not find OpenSSL version via pkg-config nor by calling the openssl binary.");
87+
writeln("\tAssuming version 1.1.0.");
88+
writeln("\tYou might need to export PKG_CONFIG_PATH or install the openssl package if you have a library-only package.");
89+
opensslVersion = "1.1.0h";
90+
}
91+
auto data = format(q{/**
92+
* Provide the version of the libssl being linked to at compile time
93+
*
94+
* This module was auto-generated by deimos/openssl's script/generate_version.d
95+
* Manual edit might get overwritten by later build.
96+
*
97+
* This module should not be directly dependend upon.
98+
* Instead, use `deimos.openssl.opensslv`, which handles explicit overrides
99+
* provides a uniform interface, and a few utilities.
100+
*/
101+
module deimos.openssl.version_;
102+
103+
/// Ditto
104+
package enum OpenSSLTextVersion = "%s";
105+
}, opensslVersion);
106+
107+
// Only write the file iff it has changed or didn't exist before.
108+
// This way timestamp-based build system will not rebuild,
109+
// and changes on the installed OpenSSL will be correctly detected.
110+
if (!target.exists || target.readText.strip != data.strip)
111+
data.toFile(target);
112+
}

source/deimos/openssl/opensslv.d

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,93 @@
1+
/**
2+
* Module to deal with the library version being used
3+
*
4+
* This library provide bindings for a wide range of OpenSSL versions,
5+
* ranging from v0.9.x to v3.0.x. Some versions are not compatible with
6+
* one another, either due to different ABI or different behavior,
7+
* for example OpenSSL 1.0 requires initialization but later versions do not.
8+
*
9+
* While things tend to mostly work or error out while linking when the version
10+
* the bindings assume and the actually C library version are too different,
11+
* we prefer to try detecting the currently used version, and allow users
12+
* to specify the version explicitly, before falling back to the latest bindings
13+
*/
114
module deimos.openssl.opensslv;
215

316
import deimos.openssl._d_util;
417

18+
version (DeimosOpenSSL_1_0_0)
19+
{
20+
// https://www.openssl.org/news/changelog.html#openssl-100
21+
// OpenSSL 1.0.0t was released 2015-12-03
22+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.0.0t";
23+
}
24+
else version (DeimosOpenSSL_1_0_1)
25+
{
26+
// https://www.openssl.org/news/changelog.html#openssl-101
27+
// OpenSSL 1.0.1u was released 2016-09-22
28+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.0.1u";
29+
}
30+
else version (DeimosOpenSSL_1_0_2)
31+
{
32+
// https://www.openssl.org/news/changelog.html#openssl-102
33+
// OpenSSL 1.0.2t was released 2019-09-10
34+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.0.2t";
35+
}
36+
else version (DeimosOpenSSL_1_1_0)
37+
{
38+
// https://www.openssl.org/news/changelog.html#openssl-110
39+
// OpenSSL 1.1.0l was released 2019-09-10
40+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.1.0l";
41+
}
42+
else version (DeimosOpenSSL_1_1_1)
43+
{
44+
// https://www.openssl.org/news/changelog.html#openssl-111
45+
// OpenSSL 1.1.1m was released 2021-12-14
46+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.1.1m";
47+
}
48+
else version (DeimosOpenSSL_3_0)
49+
{
50+
// https://www.openssl.org/news/changelog.html#openssl-30
51+
// OpenSSL 3.0.3 was released 2022-05-03
52+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"3.0.3";
53+
}
54+
else version (DeimosOpenSSLAutoDetect)
55+
{
56+
import deimos.openssl.version_;
57+
58+
public alias OpenSSLVersion = OpenSSLVersionTemplate!OpenSSLTextVersion;
59+
}
60+
else
61+
{
62+
// It was decided in https://github.com/D-Programming-Deimos/openssl/pull/66
63+
// that we should fall back to the latest supported version of the bindings,
64+
// should the user provide neither explicit version nor `DeimosOpenSSLAutoDetect`
65+
public alias OpenSSLVersion = OpenSSLVersionTemplate!"1.1.0h";
66+
}
67+
68+
// Publicly aliased above
69+
private struct OpenSSLVersionTemplate (string textVersion)
70+
{
71+
enum text = textVersion;
72+
73+
enum int major = (text[0] - '0');
74+
static assert (major >= 0);
75+
76+
enum int minor = (text[2] - '0');
77+
static assert (minor >= 0);
78+
79+
enum int patch = (text[4] - '0');
80+
static assert (patch >= 0);
81+
82+
static if (text.length == "1.1.0h".length)
83+
{
84+
enum int build = (text[5] - '`');
85+
static assert (build >= 0);
86+
}
87+
else
88+
enum int build = 0;
89+
}
90+
591
/* Numeric release version identifier:
692
* MNNFFPPS: major minor fix patch status
793
* The status nibble has one of the values 0 for development, 1 to e for betas
@@ -28,21 +114,22 @@ import deimos.openssl._d_util;
28114
*/
29115

30116
/* Version macros for compile-time API version detection */
31-
enum
32-
{
33-
OPENSSL_VERSION_MAJOR = 1,
34-
OPENSSL_VERSION_MINOR = 1,
35-
OPENSSL_VERSION_PATCH = 0,
36-
OPENSSL_VERSION_BUILD = 'h' - '`'
37-
}
117+
enum OPENSSL_VERSION_MAJOR = OpenSSLVersion.major;
118+
119+
enum OPENSSL_VERSION_MINOR = OpenSSLVersion.minor;
120+
121+
enum OPENSSL_VERSION_PATCH = OpenSSLVersion.patch;
122+
123+
enum OPENSSL_VERSION_BUILD = OpenSSLVersion.build;
38124

39125
int OPENSSL_MAKE_VERSION(int major, int minor, int patch, int build)
40126
{
41127
return (major << 28) | (minor << 20) | (patch << 12) | (build << 4) | 0xf;
42128
}
43129

44130
enum OPENSSL_VERSION_NUMBER =
45-
OPENSSL_MAKE_VERSION(OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH, OPENSSL_VERSION_BUILD);
131+
OPENSSL_MAKE_VERSION(OpenSSLVersion.major, OpenSSLVersion.minor,
132+
OpenSSLVersion.patch, OpenSSLVersion.build);
46133

47134
bool OPENSSL_VERSION_AT_LEAST(int major, int minor, int patch = 0, int build = 0)
48135
{
@@ -54,13 +141,6 @@ bool OPENSSL_VERSION_BEFORE(int major, int minor, int patch = 0, int build = 0)
54141
return OPENSSL_VERSION_NUMBER < OPENSSL_MAKE_VERSION(major, minor, patch, build);
55142
}
56143

57-
version (OPENSSL_FIPS) {
58-
enum OPENSSL_VERSION_TEXT = "OpenSSL 1.1.0h-fips 27 Mar 2018";
59-
} else {
60-
enum OPENSSL_VERSION_TEXT = "OpenSSL 1.1.0h 27 Mar 2018";
61-
}
62-
enum OPENSSL_VERSION_PTEXT = " part of " ~ OPENSSL_VERSION_TEXT;
63-
64144
/* The macros below are to be used for shared library (.so, .dll, ...)
65145
* versioning. That kind of versioning works a bit differently between
66146
* operating systems. The most usual scheme is to set a major and a minor

0 commit comments

Comments
 (0)