diff --git a/node.gyp b/node.gyp index 341ac34dce4c26..8b4b6708384892 100644 --- a/node.gyp +++ b/node.gyp @@ -93,6 +93,15 @@ 'deps/v8/tools/SourceMap.js', 'deps/v8/tools/tickprocessor-driver.js', ], + 'conditions': [ + [ 'OS=="win" and ' + 'node_use_openssl=="true" and ' + 'node_shared_openssl=="false"', { + 'use_openssl_def': 1, + }, { + 'use_openssl_def': 0, + }], + ], }, 'targets': [ @@ -291,6 +300,9 @@ '-Wl,--no-whole-archive', ], }], + ['use_openssl_def==1', { + 'sources': ['<(SHARED_INTERMEDIATE_DIR)/openssl.def'], + }], ], }], ], @@ -464,6 +476,52 @@ } }, }, + { + 'target_name': 'mkssldef', + 'type': 'none', + # TODO(bnoordhuis) Make all platforms export the same list of symbols. + # Teach mkssldef.py to generate linker maps that UNIX linkers understand. + 'conditions': [ + [ 'use_openssl_def==1', { + 'variables': { + 'mkssldef_flags': [ + # Categories to export. + '-CAES,BF,BIO,DES,DH,DSA,EC,ECDH,ECDSA,ENGINE,EVP,HMAC,MD4,MD5,' + 'NEXTPROTONEG,PSK,RC2,RC4,RSA,SHA,SHA0,SHA1,SHA256,SHA512,TLSEXT', + # Defines. + '-DWIN32', + # Symbols to filter from the export list. + '-X^DSO', + '-X^_', + '-X^private_', + ], + }, + 'conditions': [ + ['openssl_fips!=""', { + 'variables': { 'mkssldef_flags': ['-DOPENSSL_FIPS'] }, + }], + ], + 'actions': [ + { + 'action_name': 'mkssldef', + 'inputs': [ + 'deps/openssl/openssl/util/libeay.num', + 'deps/openssl/openssl/util/ssleay.num', + ], + 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/openssl.def'], + 'action': [ + 'python', + 'tools/mkssldef.py', + '<@(mkssldef_flags)', + '-o', + '<@(_outputs)', + '<@(_inputs)', + ], + }, + ], + }], + ], + }, # generate ETW header and resource files { 'target_name': 'node_etw', diff --git a/test/addons/openssl-binding/binding.cc b/test/addons/openssl-binding/binding.cc new file mode 100644 index 00000000000000..59819cd33d2a38 --- /dev/null +++ b/test/addons/openssl-binding/binding.cc @@ -0,0 +1,35 @@ +#include "node.h" +#include "../../../src/util.h" +#include "../../../src/util-inl.h" + +#include +#include + +namespace { + +inline void RandomBytes(const v8::FunctionCallbackInfo& info) { + assert(info[0]->IsArrayBufferView()); + auto view = info[0].As(); + auto byte_offset = view->ByteOffset(); + auto byte_length = view->ByteLength(); + assert(view->HasBuffer()); + auto buffer = view->Buffer(); + auto contents = buffer->GetContents(); + auto data = static_cast(contents.Data()) + byte_offset; + assert(RAND_poll()); + auto rval = RAND_bytes(data, static_cast(byte_length)); + info.GetReturnValue().Set(rval > 0); +} + +inline void Initialize(v8::Local exports, + v8::Local module, + v8::Local context) { + auto isolate = context->GetIsolate(); + auto key = v8::String::NewFromUtf8(isolate, "randomBytes"); + auto value = v8::FunctionTemplate::New(isolate, RandomBytes)->GetFunction(); + assert(exports->Set(context, key, value).IsJust()); +} + +} // anonymous namespace + +NODE_MODULE_CONTEXT_AWARE(binding, Initialize) diff --git a/test/addons/openssl-binding/binding.gyp b/test/addons/openssl-binding/binding.gyp new file mode 100644 index 00000000000000..672f84bb860a9d --- /dev/null +++ b/test/addons/openssl-binding/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': ['binding.cc'], + 'include_dirs': ['../../../deps/openssl/openssl/include'], + }, + ] +} diff --git a/test/addons/openssl-binding/test.js b/test/addons/openssl-binding/test.js new file mode 100644 index 00000000000000..aa515bac9a5c45 --- /dev/null +++ b/test/addons/openssl-binding/test.js @@ -0,0 +1,8 @@ +'use strict'; + +require('../../common'); +const assert = require('assert'); +const binding = require('./build/Release/binding'); +const bytes = new Uint8Array(1024); +assert(binding.randomBytes(bytes)); +assert(bytes.reduce((v, a) => v + a) > 0); diff --git a/tools/mkssldef.py b/tools/mkssldef.py new file mode 100755 index 00000000000000..8cbdbabd976ba9 --- /dev/null +++ b/tools/mkssldef.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +from __future__ import print_function +import re +import sys + +categories = [] +defines = [] +excludes = [] + +if __name__ == '__main__': + out = sys.stdout + filenames = sys.argv[1:] + + while filenames and filenames[0].startswith('-'): + option = filenames.pop(0) + if option == '-o': out = open(filenames.pop(0), 'w') + elif option.startswith('-C'): categories += option[2:].split(',') + elif option.startswith('-D'): defines += option[2:].split(',') + elif option.startswith('-X'): excludes += option[2:].split(',') + + excludes = map(re.compile, excludes) + exported = [] + + for filename in filenames: + for line in open(filename).readlines(): + name, _, meta, _ = re.split('\s+', line) + if any(map(lambda p: p.match(name), excludes)): continue + meta = meta.split(':') + assert meta[0] in ('EXIST', 'NOEXIST') + assert meta[2] in ('FUNCTION', 'VARIABLE') + if meta[0] != 'EXIST': continue + if meta[2] != 'FUNCTION': continue + def satisfy(expr, rules): + def test(expr): + if expr.startswith('!'): return not expr[1:] in rules + return expr == '' or expr in rules + return all(map(test, expr.split(','))) + if not satisfy(meta[1], defines): continue + if not satisfy(meta[3], categories): continue + exported.append(name) + + print('EXPORTS', file=out) + for name in sorted(exported): print(' ', name, file=out)