Friday, May 13, 2011

64 Bit SPDY Successed

‹prev | My Chain | next›

It would seem the Ubuntu / Debian guys have solved the problem of compiling openssl on 64 bit platforms. More specifically, the upcoming Oneiric Ocelot 11.10 version of Ubuntu will include openssl version 1.0. That is good news for SPDY developers in 6 months, but also good news for me. Since they have already built the packages, it stands to reason that it is possible.

I clear out my failed attempts to compile openssl:
make clean
...and try Oneiric's command line arguments to openssl's Configure:
./Configure shared --prefix=$HOME/local no-idea no-mdc2 no-rc5 zlib  enable-tlsext no-ssl2 linux-x86_64
New in there are the arguments in bold:
# [no-]shared   [don't] try to create shared libraries when supported.
# no- build without specified algorithm (rsa, idea, rc5, ...)
# no-idea - no-cipher (see above)
# no-mdc - same as above???
# no-rc5 - no-cipher (see above)
# [no-]zlib [don't] compile support for zlib compression.
# enable-tlsext - ???
# no-ssl2 - seems obvious
The shared option would have saved me explicitly trying to make the libssl.so.1.1.0 target endlessly yesterday. Still I do not think that would have generated my relocation R_X86_64_32 woes. It seems more likely that one of the ciphers or ssl2 would have been the cause. Anyway, let's make sure that this actually does change something:
➜  openssl  make
make[2]: Entering directory `/home/cstrom/repos/openssl/test'
make[2]: Leaving directory `/home/cstrom/repos/openssl/test'
make[2]: Entering directory `/home/cstrom/repos/openssl/test'
make[2]: Leaving directory `/home/cstrom/repos/openssl/test'
make[1]: Leaving directory `/home/cstrom/repos/openssl/test'
making all in tools...
make[1]: Entering directory `/home/cstrom/repos/openssl/tools'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/home/cstrom/repos/openssl/tools'
➜ openssl
So far so good. Any shared objects?
➜  openssl  find . -name \*.so\* 
./libssl.so
./libcrypto.so.1.1.0
./engines/libubsec.so
./engines/lib4758cca.so
./engines/libsureware.so
./engines/libgmp.so
./engines/libatalla.so
./engines/libpadlock.so
./engines/libchil.so
./engines/libnuron.so
./engines/libcswift.so
./engines/libaep.so
./engines/libcapi.so
./engines/ccgost/libgost.so
./crypto/des/times/486-50.sol
./libcrypto.so
./libssl.so.1.1.0
Progress!

Installing gives way too much information:
make install
...
make[1]: Leaving directory `/home/cstrom/local/lib'
OpenSSL shared libraries have been installed in:
/home/cstrom/local

If this directory is not in a standard system path for dynamic/shared
libraries, then you will have problems linking and executing
applications that use OpenSSL libraries UNLESS:

* you link with static (archive) libraries. If you are truly
paranoid about security, you should use static libraries.
* you use the GNU libtool code during linking
(http://www.gnu.org/software/libtool/libtool.html)
* you use pkg-config during linking (this requires that
PKG_CONFIG_PATH includes the path to the OpenSSL shared
library directory), and make use of -R or -rpath.
(http://www.freedesktop.org/software/pkgconfig/)
* you specify the system-wide link path via a command such
as crle(1) on Solaris systems.
* you add the OpenSSL shared library directory to /etc/ld.so.conf
and run ldconfig(8) on Linux systems.
* you define the LD_LIBRARY_PATH, LIBPATH, SHLIB_PATH (HP),
DYLD_LIBRARY_PATH (MacOS X) or PATH (Cygwin and DJGPP)
environment variable and add the OpenSSL shared library
directory to it.

One common tool to check the dynamic dependencies of an executable
or dynamic library is ldd(1) on most UNIX systems.

See any operating system documentation and manpages about shared
libraries for your version of UNIX. The following manpages may be
helpful: ld(1), ld.so(1), ld.so.1(1) [Solaris], dld.sl(1) [HP],
ldd(1), crle(1) [Solaris], pldd(1) [Solaris], ldconfig(8) [Linux],
chatr(1) [HP].
cp libcrypto.pc /home/cstrom/local/lib/pkgconfig
chmod 644 /home/cstrom/local/lib/pkgconfig/libcrypto.pc
cp libssl.pc /home/cstrom/local/lib/pkgconfig
chmod 644 /home/cstrom/local/lib/pkgconfig/libssl.pc
cp openssl.pc /home/cstrom/local/lib/pkgconfig
chmod 644 /home/cstrom/local/lib/pkgconfig/openssl.pc
Yes, I am compiling and installing this myself into a non-standard directory. Sheesh. I already have the necessary environment variables to handle this:
PATH=$HOME/local/bin/:$PATH
LD_LIBRARY_PATH=$HOME/local/lib
PKG_CONFIG_PATH=$HOME/local/lib/pkgconfig
CPATH=$HOME/repos/local/include
So I ought to be ready to install node.js with the latest, NPN-capable openssl. I configure node.js with references to this openssl:
 ➜  node git:(master) ./configure --openssl-includes=$HOME/local/include/openssl --openssl-libpath=$HOME/local/lib --prefix=$HOME/local/node-v0.5.0-pre
Checking for program g++ or c++ : /usr/bin/g++
Checking for program cpp : /usr/bin/cpp
Checking for program ar : /usr/bin/ar
Checking for program ranlib : /usr/bin/ranlib
Checking for g++ : ok
Checking for program gcc or cc : /usr/bin/gcc
Checking for gcc : ok
Checking for library dl : yes
Checking for openssl : yes
...
Running make, however crashes on missing SSLv2 references:
make
...
[75/75] cxx_link: build/default/src/node_main_4.o build/default/src/node_4.o build/default/src/node_buffer_4.o build/default/src/node_javascript_4.o build/
default/src/node_extensions_4.o build/default/src/node_http_parser_4.o build/default/src/node_net_4.o build/default/src/node_io_watcher_4.o build/default/s
rc/node_constants_4.o build/default/src/node_cares_4.o build/default/src/node_events_4.o build/default/src/node_file_4.o build/default/src/node_signal_watc
her_4.o build/default/src/node_stat_watcher_4.o build/default/src/node_timer_4.o build/default/src/node_script_4.o build/default/src/node_os_4.o build/defa
ult/src/node_dtrace_4.o build/default/src/node_string_4.o build/default/src/node_stdio_4.o build/default/src/node_child_process_4.o build/default/src/platf
orm_linux_4.o build/default/src/node_crypto_4.o build/default/deps/libeio/eio_1.o build/default/deps/http_parser/http_parser_2.o build/default/deps/libev/e
v_1.o build/default/deps/c-ares/ares_strcasecmp_1.o build/default/deps/c-ares/ares_free_string_1.o build/default/deps/c-ares/ares_options_1.o build/default
/deps/c-ares/ares_send_1.o build/default/deps/c-ares/ares_parse_txt_reply_1.o build/default/deps/c-ares/ares_parse_ptr_reply_1.o build/default/deps/c-ares/
ares_nowarn_1.o build/default/deps/c-ares/ares_search_1.o build/default/deps/c-ares/ares_gethostbyname_1.o build/default/deps/c-ares/ares_getsock_1.o build
/default/deps/c-ares/ares__timeval_1.o build/default/deps/c-ares/inet_ntop_1.o build/default/deps/c-ares/ares_parse_a_reply_1.o build/default/deps/c-ares/a
res_getopt_1.o build/default/deps/c-ares/ares__close_sockets_1.o build/default/deps/c-ares/ares_expand_string_1.o build/default/deps/c-ares/ares_destroy_1.
o build/default/deps/c-ares/ares_cancel_1.o build/default/deps/c-ares/ares_parse_aaaa_reply_1.o build/default/deps/c-ares/ares_parse_ns_reply_1.o build/def
ault/deps/c-ares/ares_version_1.o build/default/deps/c-ares/ares__get_hostent_1.o build/default/deps/c-ares/ares_writev_1.o build/default/deps/c-ares/ares_
expand_name_1.o build/default/deps/c-ares/ares_free_hostent_1.o build/default/deps/c-ares/ares_parse_mx_reply_1.o build/default/deps/c-ares/ares_gethostbya
ddr_1.o build/default/deps/c-ares/ares_query_1.o build/default/deps/c-ares/ares_data_1.o build/default/deps/c-ares/ares_init_1.o build/default/deps/c-ares/
ares_timeout_1.o build/default/deps/c-ares/ares_fds_1.o build/default/deps/c-ares/inet_net_pton_1.o build/default/deps/c-ares/ares_strerror_1.o build/default/deps/c-ares/bitncmp_1.o build/default/deps/c-ares/ares_getnameinfo_1.o build/default/deps/c-ares/ares_library_init_1.o build/default/deps/c-ares/ares_mkquery_1.o build/default/deps/c-ares/ares_process_1.o build/default/deps/c-ares/windows_port_1.o build/default/deps/c-ares/ares_parse_srv_reply_1.o build/default/deps/c-ares/ares_llist_1.o build/default/deps/c-ares/ares__read_line_1.o build/default/deps/c-ares/ares_strdup_1.o -> build/default/node
/usr/bin/g++ default/src/node_main_4.o default/src/node_4.o default/src/node_buffer_4.o default/src/node_javascript_4.o default/src/node_extensions_4.o default/src/node_http_parser_4.o default/src/node_net_4.o default/src/node_io_watcher_4.o default/src/node_constants_4.o default/src/node_cares_4.o default/src/node_events_4.o default/src/node_file_4.o default/src/node_signal_watcher_4.o default/src/node_stat_watcher_4.o default/src/node_timer_4.o default/src/node_script_4.o default/src/node_os_4.o default/src/node_dtrace_4.o default/src/node_string_4.o default/src/node_stdio_4.o default/src/node_child_process_4.o default/src/platform_linux_4.o default/src/node_crypto_4.o default/deps/libeio/eio_1.o default/deps/http_parser/http_parser_2.o default/deps/libev/ev_1.o default/deps/c-ares/ares_strcasecmp_1.o default/deps/c-ares/ares_free_string_1.o default/deps/c-ares/ares_options_1.o default/deps/c-ares/ares_send_1.o default/deps/c-ares/ares_parse_txt_reply_1.o default/deps/c-ares/ares_parse_ptr_reply_1.o default/deps/c-ares/ares_nowarn_1.o default/deps/c-ares/ares_search_1.o default/deps/c-ares/ares_gethostbyname_1.o default/deps/c-ares/ares_getsock_1.o default/deps/c-ares/ares__timeval_1.o default/deps/c-ares/inet_ntop_1.o default/deps/c-ares/ares_parse_a_reply_1.o default/deps/c-ares/ares_getopt_1.o default/deps/c-ares/ares__close_sockets_1.o default/deps/c-ares/ares_expand_string_1.o default/deps/c-ares/ares_destroy_1.o default/deps/c-ares/ares_cancel_1.o default/deps/c-ares/ares_parse_aaaa_reply_1.o default/deps/c-ares/ares_parse_ns_reply_1.o default/deps/c-ares/ares_version_1.o default/deps/c-ares/ares__get_hostent_1.o default/deps/c-ares/ares_writev_1.o default/deps/c-ares/ares_expand_name_1.o default/deps/c-ares/ares_free_hostent_1.o default/deps/c-ares/ares_parse_mx_reply_1.o default/deps/c-ares/ares_gethostbyaddr_1.o default/deps/c-ares/ares_query_1.o default/deps/c-ares/ares_data_1.o default/deps/c-ares/ares_init_1.o default/deps/c-ares/ares_timeout_1.o default/deps/c-ares/ares_fds_1.o default/deps/c-ares/inet_net_pton_1.o default/deps/c-ares/ares_strerror_1.o default/deps/c-ares/bitncmp_1.o default/deps/c-ares/ares_getnameinfo_1.o default/deps/c-ares/ares_library_init_1.o default/deps/c-ares/ares_mkquery_1.o default/deps/c-ares/ares_process_1.o default/deps/c-ares/windows_port_1.o default/deps/c-ares/ares_parse_srv_reply_1.o default/deps/c-ares/ares_llist_1.o default/deps/c-ares/ares__read_line_1.o default/deps/c-ares/ares_strdup_1.o -o /home/cstrom/repos/node/build/default/node -pthread -rdynamic /home/cstrom/repos/node/build/default/libv8.a -L/home/cstrom/local/lib -L/usr/lib -L/usr/local/lib -Wl,-Bdynamic -lrt -lssl -lcrypto -ldl -lutil
default/src/node_crypto_4.o: In function `node::crypto::SecureContext::Init(v8::Arguments const&)':
/home/cstrom/repos/node/build/../src/node_crypto.cc:102: undefined reference to `SSLv2_method'
/home/cstrom/repos/node/build/../src/node_crypto.cc:104: undefined reference to `SSLv2_server_method'
/home/cstrom/repos/node/build/../src/node_crypto.cc:106: undefined reference to `SSLv2_client_method'
collect2: ld returned 1 exit status

Waf: Leaving directory `/home/cstrom/repos/node/build'
Build failed: -> task failed (err #1):
{task: cxx_link node_main_4.o,node_4.o,node_buffer_4.o,node_javascript_4.o,node_extensions_4.o,node_http_parser_4.o,node_net_4.o,node_io_watcher_4.o,node_constants_4.o,node_cares_4.o,node_events_4.o,node_file_4.o,node_signal_watcher_4.o,node_stat_watcher_4.o,node_timer_4.o,node_script_4.o,node_os_4.o,node_dtrace_4.o,node_string_4.o,node_stdio_4.o,node_child_process_4.o,platform_linux_4.o,node_crypto_4.o,eio_1.o,http_parser_2.o,ev_1.o,ares_strcasecmp_1.o,ares_free_string_1.o,ares_options_1.o,ares_send_1.o,ares_parse_txt_reply_1.o,ares_parse_ptr_reply_1.o,ares_nowarn_1.o,ares_search_1.o,ares_gethostbyname_1.o,ares_getsock_1.o,ares__timeval_1.o,inet_ntop_1.o,ares_parse_a_reply_1.o,ares_getopt_1.o,ares__close_sockets_1.o,ares_expand_string_1.o,ares_destroy_1.o,ares_cancel_1.o,ares_parse_aaaa_reply_1.o,ares_parse_ns_reply_1.o,ares_version_1.o,ares__get_hostent_1.o,ares_writev_1.o,ares_expand_name_1.o,ares_free_hostent_1.o,ares_parse_mx_reply_1.o,ares_gethostbyaddr_1.o,ares_query_1.o,ares_data_1.o,ares_init_1.o,ares_timeout_1.o,ares_fds_1.o,inet_net_pton_1.o,ares_strerror_1.o,bitncmp_1.o,ares_getnameinfo_1.o,ares_library_init_1.o,ares_mkquery_1.o,ares_process_1.o,windows_port_1.o,ares_parse_srv_reply_1.o,ares_llist_1.o,ares__read_line_1.o,ares_strdup_1.o -> node}
make: *** [program] Error 1
Before trying anything too drastic, I distclean and go again:
make distclean
./configure --openssl-includes=$HOME/local/include/openssl --openssl-libpath=$HOME/local/lib --prefix=$HOME/local/node-v0.5.0-pre
make
But unfortunately no change. Since there are no node.js configuration options for compiling without SSLv2, it is back to openssl. Let's see if it compiles with SSLv2 (by removing the no-ssl2 option to Configure):
make clean
./Configure shared --prefix=$HOME/local no-idea no-mdc2 no-rc5 zlib enable-tlsext linux-x86_64
make depend
make
...
making all in tools...
make[1]: Entering directory `/home/cstrom/repos/openssl/tools'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/home/cstrom/repos/openssl/tools'
make install
Ooh! Success, how about node.js?
➜  node git:(master) make
Waf: Leaving directory `/home/cstrom/repos/node/build'
'build' finished successfully (1.160s)
Woo hoo!

So I am finally ready to run the node-spdy test server:
➜  test git:(master) ✗ node spdy-server.js 
node: error while loading shared libraries: libssl.so.1.1.0: cannot open shared object file: No such file or directory
Oh, for the love of...

It would seem that zsh does not honory LD_LIBRARY_PATH. I will figure that out another day (maybe). For now, I switch to bash:
1.9.2@spdy ~/repos/node-spdy/test (master)$ node spdy-server.js

node.js:183
throw e; // process.nextTick error, or 'error' event on first tick
^
You're using not NPN-enabled version of node.js
Are you freaking kidding me?!

I finally have openssl compiling on 64 bit Ubunutu. I have node configured to link against it:
1.9.2@spdy ~/repos/node-spdy/test (master)$ ldd `which node`
linux-vdso.so.1 => (0x00007fff52fa4000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9a142f2000)
libssl.so.1.1.0 => /home/cstrom/local/lib/libssl.so.1.1.0 (0x00007f9a1408f000)
libcrypto.so.1.1.0 => /home/cstrom/local/lib/libcrypto.so.1.1.0 (0x00007f9a13cc5000)

libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9a13ac1000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f9a138be000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9a135b7000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9a13332000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9a1311c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9a12efd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a12b69000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a14522000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f9a12951000)
And it is still not working? Grr...

Tracing the error back, node-spdy throws that exception if tls.NPN_ENABLED is not set:
if (!tls.NPN_ENABLED) throw 'You\'re using not NPN-enabled version of node.js';
NPN_ENABLED is set if OPENSSL_NPN_NEGOTIATED is defined:
#ifdef OPENSSL_NPN_NEGOTIATED
#define NPN_ENABLED 1
NODE_DEFINE_CONSTANT(target, NPN_ENABLED);
#endif
OPENSSL_NPN_NEGOTIATED is defined if OPENSSL_NO_NEXTPROTONEG is not set:
#ifndef OPENSSL_NO_NEXTPROTONEG
...
#define OPENSSL_NPN_UNSUPPORTED 0
#define OPENSSL_NPN_NEGOTIATED 1
#define OPENSSL_NPN_NO_OVERLAP 2

#endif
As best I can tell, OPENSSL_NO_NEXTPROTONEG is, in fact, not set anywhere:
➜  openssl  grep -r OPENSSL_NO_NEXTPROTONEG . | grep defined\\b
./ssl/s3_clnt.c:#if defined(OPENSSL_NO_TLSEXT) || defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_clnt.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_srvr.c:#if defined(OPENSSL_NO_TLSEXT) || defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_srvr.c:#if defined(OPENSSL_NO_TLSEXT) || defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_srvr.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_srvr.c:#if defined(OPENSSL_NO_TLSEXT) || defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/s3_lib.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./ssl/ssl_lib.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/s_server.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/s_server.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/apps.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/s_client.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/s_client.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
./apps/s_client.c:#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
➜ openssl grep -r OPENSSL_NO_NEXTPROTONEG . | grep define\\b
➜ openssl
Ugh. So my only guess at this point is that I am not, in fact, including the openssl headers that I think I am. If that is the case, then, regardless of whether or not OPENSSL_NO_NEXTPROTONEG is defined, nothing is going to define OPENSSL_NPN_NEGOTIATED. Hrm... maybe --openssl-includes=$HOME/local/include/openssl is wrong. Perhaps instead, I need to omit the openssl (the switch then becomes --openssl-includes=$HOME/local/include):
➜  node git:(master) ./configure --openssl-includes=$HOME/local/include --openssl-libpath=$HOME/local/lib --prefix=$HOME/local/node-v0.5.0-pre
Checking for program g++ or c++ : /usr/bin/g++
Checking for program cpp : /usr/bin/cpp
Checking for program ar : /usr/bin/ar
Checking for program ranlib : /usr/bin/ranlib
Checking for g++ : ok
Checking for program gcc or cc : /usr/bin/gcc
Checking for gcc : ok
Checking for library dl : yes
Checking for function SSL_library_init : yes
Checking for header openssl/crypto.h : yes
Checking for library util : yes
Checking for library rt : yes
Checking for CLOCK_MONOTONIC : yes
...
Promising. I had not noticed before that configure was looking for openssl/crypto.h like that. By including the openssl in the command line switch, I may have been making it impossible for configure to find my local ssl.h. After compilation finishes successfully and I install, I am again ready to try the test node-spdy server:
1.9.2@spdy ~/repos/node-spdy/test (master)$ node spdy-server.js 

node.js:183
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'connect'
at Function._resolveFilename (module.js:322:11)
at Function._load (module.js:267:25)
at require (module.js:367:19)
at Object.<anonymous> (/home/cstrom/repos/node-spdy/test/spdy-server.js:17:14)
at Module._compile (module.js:423:26)
at Object..js (module.js:429:10)
at Module.load (module.js:339:31)
at Function._load (module.js:298:12)
at Array.<anonymous> (module.js:442:10)
at EventEmitter._tickCallback (node.js:175:26)
Not successful, but yay! I seem to have an NPN-enabled node running on 64 bit linux.

Now about that connect error... Perhaps that is the middleware framework. It (probably) won't hurt to try it:
1.9.2@spdy ~/repos/node-spdy/test (master)$ npm install connect
mime@1.2.2 ../node_modules/connect/node_modules/mime
qs@0.1.0 ../node_modules/connect/node_modules/qs
connect@1.4.1 ../node_modules/connect
And once again, let's start up the server:
1.9.2@spdy ~/repos/node-spdy/test (master)$ node spdy-server.js 
TLS NPN Server is running on port : 8081
Success!

Wow. That was an unexpectedly long journey. But worth it as now I seem to have solved my 64 bit woes. I will pick back up tomorrow verifying that I can, indeed, run 64 bit ruby NPN. As a stretch goal for tomorrow, I would like to examine the Next Protocol Negotiation in a bit more detail. For now, I call it a night in the hopes to catch a little of the Neil Gaiman Dr. Who episode. Awesome sauce!


Day #20

No comments:

Post a Comment