Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try out using ImportC to read the C headers instead of needing to translate them #100

Open
WalterBright opened this issue Feb 19, 2023 · 3 comments

Comments

@WalterBright
Copy link
Contributor

and post any issues found to bugzilla.

@CyberShadow
Copy link
Member

CyberShadow commented Mar 3, 2023

I tried this and got a simple program working. What stands out:

  1. Because on the C side everything is in one global namespace, if any header imports stdio.h then all those declarations become intermixed with the declarations which we actually want. As such we have to either use selective imports or switch over to using the C declarations completely, foregoing those in Druntime (which does make it more difficult to interop e.g. with std.stdio).

  2. Constants which are defined using #define are not visible on the D side. But only some? Not sure how this works. E.g. OPENSSL_VERSION_STR was visible, but AF_INET or SSL_FILETYPE_PEM was not.

  3. Function-like macros are not visible. This is a major problem and is used heavily by OpenSSL, especially as it historically moved the ABI around a lot and provided source-compatibility shims using C macros.

Here is the C side (c_includes.c):

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/tls1.h>
#include <openssl/err.h>

static const typeof(AF_INET) _AF_INET = AF_INET;
#undef AF_INET
static const typeof(_AF_INET) AF_INET = _AF_INET;

static const typeof(INADDR_ANY) _INADDR_ANY = INADDR_ANY;
#undef INADDR_ANY
static const typeof(_INADDR_ANY) INADDR_ANY = _INADDR_ANY;

static const typeof(SSL_FILETYPE_PEM) _SSL_FILETYPE_PEM = SSL_FILETYPE_PEM;
#undef SSL_FILETYPE_PEM
static const typeof(_SSL_FILETYPE_PEM) SSL_FILETYPE_PEM = _SSL_FILETYPE_PEM;

inline long _SSL_set_tlsext_host_name(SSL* ssl, const char* name) { return SSL_set_tlsext_host_name(ssl, name); }
#undef SSL_set_tlsext_host_name
inline long SSL_set_tlsext_host_name(SSL* ssl, const char* name) { return _SSL_set_tlsext_host_name(ssl, name); }

static int OPENSSL_MAKE_VERSION(int major, int minor, int patch, int build)
{
    return (major << 28) | (minor << 20) | (patch << 12) | (build << 4) | 0xf;
}
static /*bool*/int OPENSSL_VERSION_AT_LEAST(int major, int minor, int patch/* = 0*/, int build/* = 0*/)
{
    return OPENSSL_VERSION_NUMBER >= OPENSSL_MAKE_VERSION(major, minor, patch, build);
}

OPENSSL_MAKE_VERSION and OPENSSL_VERSION_AT_LEAST are inventions of this Deimos package and can be ignored. The rest are redefining constants/functions that are defined as preprocessor macros as C definitions instead, so they're visible from D.

The D side is the sslecho example, I had to do these modifications:

--- /home/vladimir/work/extern/deimos-openssl/examples/sslecho/source/app.d	2022-11-19 23:14:04.256095751 +0000
+++ test.d	2023-03-03 22:06:56.622096656 +0000
@@ -1,18 +1,11 @@
 /// https://github.com/openssl/openssl/tree/master/demos/sslecho
 module app;
 
-import core.stdc.stdio;
-import core.sys.posix.netinet.in_;
-import core.sys.posix.stdio : getline;
-import core.sys.posix.unistd;
+import c_includes;
 
 import std.algorithm : startsWith;
 import std.stdio : write, writeln, writefln;
 
-import deimos.openssl.opensslv;
-import deimos.openssl.err;
-import deimos.openssl.ssl;
-
 const ushort server_port = 4433;
 
 int create_socket(bool isServer)
@@ -75,7 +68,7 @@
 
     /* Splash */
     writefln("sslecho : Simple Echo Client/Server (OpenSSL %s): %s %s",
-             OpenSSLVersion.text, __DATE__, __TIME__);
+             OPENSSL_VERSION_STR[], __DATE__, __TIME__);
 
     /* Need to know if client or server */
     if (args.length < 2)
@@ -99,7 +92,7 @@
 
     writeln("We are the server on port: ", port);
 
-    const SSL_METHOD* method = TLS_server_method();
+    SSL_METHOD* method = TLS_server_method();
     SSL_CTX* ctx = SSL_CTX_new(method);
     if (ctx is null)
     {
@@ -196,7 +189,7 @@
     char[256] buffer;
     writeln("We are the client");
 
-    const SSL_METHOD* method = TLS_client_method();
+    SSL_METHOD* method = TLS_client_method();
     SSL_CTX* ctx = SSL_CTX_new(method);
     if (ctx is null)
     {
@@ -251,7 +244,7 @@
     /* Set host name for SNI */
     SSL_set_tlsext_host_name(ssl, remote.ptr);
     /* Configure server hostname check */
-    static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0))
+    static if (OPENSSL_VERSION_AT_LEAST(1, 1, 0, 0))
         SSL_set1_host(ssl, remote.ptr);
 
     /* Now do SSL connect with server */

@WalterBright
Copy link
Contributor Author

Thank you, this is valuable feedback. Even if we can't import everything directly from the .h file (like the macros for inline functions) we should be able to greatly reduce the need to translate the whole file.

@WalterBright
Copy link
Contributor Author

Constants which are defined using #define are not visible on the D side. But only some?

Currently, if they expand to a single token that's a literal, they get treated like:

#define ABC 3

to:

enum ABC = 3;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants