quic: update the guard to check openssl version

Since we need to be able to use the openssl adapter provided
by the ngtcp2 library, and because that adapter does not include
any compile guards to ensure that OpenSSL 3.5 is being used and
that the APIs are actually available, we need to add a compile
time check for the openssl version in order to conditionally
include the adapter to avoid build errors when using a shared
openssl library that is not OpenSSL 3.5.

Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/59249
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
James M Snell 2025-07-27 14:34:12 -07:00
parent ebfc28a037
commit 99c80e3a45
3 changed files with 92 additions and 10 deletions

View file

@ -1205,6 +1205,65 @@ def get_gas_version(cc):
warn(f'Could not recognize `gas`: {gas_ret}')
return '0.0'
def get_openssl_version():
"""Parse OpenSSL version from opensslv.h header file.
Returns the version as a number matching OPENSSL_VERSION_NUMBER format:
0xMNN00PPSL where M=major, NN=minor, PP=patch, S=status(0xf=release,0x0=pre), L=0
"""
try:
# Use the C compiler to extract preprocessor macros from opensslv.h
args = ['-E', '-dM', '-include', 'openssl/opensslv.h', '-']
if not options.shared_openssl:
args = ['-I', 'deps/openssl/openssl/include'] + args
elif options.shared_openssl_includes:
args = ['-I', options.shared_openssl_includes] + args
proc = subprocess.Popen(
shlex.split(CC) + args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
with proc:
proc.stdin.write(b'\n')
out = to_utf8(proc.communicate()[0])
if proc.returncode != 0:
warn('Failed to extract OpenSSL version from opensslv.h header')
return 0
# Parse the macro definitions
macros = {}
for line in out.split('\n'):
if line.startswith('#define OPENSSL_VERSION_'):
parts = line.split()
if len(parts) >= 3:
macro_name = parts[1]
macro_value = parts[2]
macros[macro_name] = macro_value
# Extract version components
major = int(macros.get('OPENSSL_VERSION_MAJOR', '0'))
minor = int(macros.get('OPENSSL_VERSION_MINOR', '0'))
patch = int(macros.get('OPENSSL_VERSION_PATCH', '0'))
# Check if it's a pre-release (has non-empty PRE_RELEASE string)
pre_release = macros.get('OPENSSL_VERSION_PRE_RELEASE', '""').strip('"')
status = 0x0 if pre_release else 0xf
# Construct version number: 0xMNN00PPSL
version_number = ((major << 28) |
(minor << 20) |
(patch << 4) |
status)
return version_number
except (OSError, ValueError, subprocess.SubprocessError) as e:
warn(f'Failed to determine OpenSSL version from header: {e}')
return 0
# Note: Apple clang self-reports as clang 4.2.0 and gcc 4.2.1. It passes
# the version check more by accident than anything else but a more rigorous
# check involves checking the build number against an allowlist. I'm not
@ -1828,6 +1887,8 @@ def configure_openssl(o):
if options.quic:
o['defines'] += ['NODE_OPENSSL_HAS_QUIC']
o['variables']['openssl_version'] = get_openssl_version()
configure_library('openssl', o)
def configure_sqlite(o):

View file

@ -129,6 +129,36 @@
'HAVE_NETINET_IN_H',
],
}],
# TODO: Support OpenSSL 3.5 shared library builds.
# The complexity here is that we need to use the ngtcp2 ossl
# adapter, which does not include any conditional checks to
# see if the version of OpenSSL used has the necessary QUIC
# APIs, so we need to ensure that we conditionally enable use
# of the adapter only when we know that the OpenSSL version we
# are compiling against has the necessary APIs. We can do that
# by checkig the OpenSSL version number but, currently, the
# code that does so checks only the VERSION.dat file that is
# bundled with the openssl dependency. We'll need to update
# that to support the shared library case, where the version
# of the shared library needs to be determined.
#
# TODO: Support Boringssl here also. ngtcp2 provides an adapter
# for Boringssl. If we can detect that boringssl is being used
# here then we can use that adapter and also set the
# QUIC_NGTCP2_USE_BORINGSSL define (the guard in quic/guard.h
# would need to be updated to check for this define).
['node_shared_openssl=="false" and openssl_version >= 0x3050001f', {
'sources': [
'<@(ngtcp2_sources_ossl)',
],
'direct_dependent_settings': {
'defines': [
# Tells us that we are using the OpenSSL 3.5 adapter
# that is provided by ngtcp2.
'QUIC_NGTCP2_USE_OPENSSL_3_5',
],
},
}]
],
'direct_dependent_settings': {
'defines': [
@ -143,7 +173,6 @@
},
'sources': [
'<@(ngtcp2_sources)',
'<@(ngtcp2_sources_ossl)',
]
},
{

View file

@ -1,13 +1,5 @@
#pragma once
#if HAVE_OPENSSL
#include <openssl/opensslv.h>
// QUIC is only available in Openssl 3.5.x and later. It was not introduced in
// Node.js until 3.5.1... prior to that we will not compile any of the QUIC
// related code.
#if OPENSSL_VERSION_NUMBER < 0x30500010 || OPENSSL_IS_BORINGSSL
#define OPENSSL_NO_QUIC = 1
#endif
#else
#ifndef QUIC_NGTCP2_USE_OPENSSL_3_5
#define OPENSSL_NO_QUIC = 1
#endif