/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "sd-dlopen.h"

#include "log.h"                /* IWYU pragma: keep */
#include "ssl-util.h"
#include "strv.h"

#if HAVE_OPENSSL

DLSYM_PROTOTYPE(SSL_ctrl) = NULL;
DLSYM_PROTOTYPE(SSL_CTX_ctrl) = NULL;
DLSYM_PROTOTYPE(SSL_CTX_free) = NULL;
DLSYM_PROTOTYPE(SSL_CTX_new) = NULL;
DLSYM_PROTOTYPE(SSL_CTX_set_default_verify_paths) = NULL;
DLSYM_PROTOTYPE(SSL_CTX_set_options) = NULL;
DLSYM_PROTOTYPE(SSL_do_handshake) = NULL;
DLSYM_PROTOTYPE(SSL_free) = NULL;
DLSYM_PROTOTYPE(SSL_get_error) = NULL;
DLSYM_PROTOTYPE(SSL_get_wbio) = NULL;
DLSYM_PROTOTYPE(SSL_get0_param) = NULL;
DLSYM_PROTOTYPE(SSL_get1_session) = NULL;
DLSYM_PROTOTYPE(SSL_new) = NULL;
DLSYM_PROTOTYPE(SSL_read) = NULL;
DLSYM_PROTOTYPE(SSL_SESSION_free) = NULL;
DLSYM_PROTOTYPE(SSL_set_bio) = NULL;
DLSYM_PROTOTYPE(SSL_set_connect_state) = NULL;
DLSYM_PROTOTYPE(SSL_set_session) = NULL;
DLSYM_PROTOTYPE(SSL_set_verify) = NULL;
DLSYM_PROTOTYPE(SSL_shutdown) = NULL;
DLSYM_PROTOTYPE(SSL_write) = NULL;
DLSYM_PROTOTYPE(TLS_client_method) = NULL;

#endif

int dlopen_libssl(int log_level) {
#if HAVE_OPENSSL
        static void *libssl_dl = NULL;
        int r;

        LIBSSL_NOTE(SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED);

        // FIXME: switch order to prefer libssl.so.4 in a future version once it has stabilized
        FOREACH_STRING(soname, "libssl.so.3", "libssl.so.4") {
                r = dlopen_many_sym_or_warn(
                                &libssl_dl,
                                soname,
                                LOG_DEBUG,
                                DLSYM_ARG(SSL_ctrl),
                                DLSYM_ARG(SSL_CTX_ctrl),
                                DLSYM_ARG(SSL_CTX_free),
                                DLSYM_ARG(SSL_CTX_new),
                                DLSYM_ARG(SSL_CTX_set_default_verify_paths),
                                DLSYM_ARG(SSL_CTX_set_options),
                                DLSYM_ARG(SSL_do_handshake),
                                DLSYM_ARG(SSL_free),
                                DLSYM_ARG(SSL_get_error),
                                DLSYM_ARG(SSL_get_wbio),
                                DLSYM_ARG(SSL_get0_param),
                                DLSYM_ARG(SSL_get1_session),
                                DLSYM_ARG(SSL_new),
                                DLSYM_ARG(SSL_read),
                                DLSYM_ARG(SSL_SESSION_free),
                                DLSYM_ARG(SSL_set_bio),
                                DLSYM_ARG(SSL_set_connect_state),
                                DLSYM_ARG(SSL_set_session),
                                DLSYM_ARG(SSL_set_verify),
                                DLSYM_ARG(SSL_shutdown),
                                DLSYM_ARG(SSL_write),
                                DLSYM_ARG(TLS_client_method));
                if (r >= 0)
                        break;
        }
        if (r < 0) {
                log_full_errno(log_level, r, "Neither libssl.so.4 nor libssl.so.3 could be loaded");
                return -EOPNOTSUPP; /* turn into recognizable error */
        }

        return 0;
#else
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
                              "libssl support is not compiled in.");
#endif
}
