diff options
| author | Edward Thomson <ethomson@edwardthomson.com> | 2018-11-18 10:29:07 +0000 |
|---|---|---|
| committer | Edward Thomson <ethomson@edwardthomson.com> | 2018-11-28 15:46:57 +0000 |
| commit | df2cc1087f6de8718319e5bcc65ca8e0e07b717e (patch) | |
| tree | 02264b646329a5eeca8940fa8557e714e303a50f | |
| parent | 0467606ff4dbf57401c8b58188652df821ec865b (diff) | |
| download | libgit2-df2cc1087f6de8718319e5bcc65ca8e0e07b717e.tar.gz | |
stream: provide generic registration API
Update the new stream registration API to be `git_stream_register`
which takes a registration structure and a TLS boolean. This allows
callers to register non-TLS streams as well as TLS streams.
Provide `git_stream_register_tls` that takes just the init callback for
backward compatibliity.
| -rw-r--r-- | include/git2/sys/stream.h | 47 | ||||
| -rw-r--r-- | src/global.c | 4 | ||||
| -rw-r--r-- | src/streams/registry.c | 99 | ||||
| -rw-r--r-- | src/streams/registry.h | 19 | ||||
| -rw-r--r-- | src/streams/socket.c | 34 | ||||
| -rw-r--r-- | src/streams/tls.c | 77 | ||||
| -rw-r--r-- | src/streams/tls.h | 3 | ||||
| -rw-r--r-- | tests/core/stream.c | 63 |
8 files changed, 262 insertions, 84 deletions
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h index 104ec3b5c..edbe66ff8 100644 --- a/include/git2/sys/stream.h +++ b/include/git2/sys/stream.h @@ -45,9 +45,9 @@ typedef struct { int version; /** - * Called to create a new TLS connection to a given host. + * Called to create a new connection to a given host. * - * @param out The created TLS stream + * @param out The created stream * @param host The hostname to connect to; may be a hostname or * IP address * @param port The port to connect to; may be a port number or @@ -57,11 +57,12 @@ typedef struct { int (*init)(git_stream **out, const char *host, const char *port); /** - * Called to create a new TLS connection on top of the given - * stream. May be used to proxy a TLS stream over a CONNECT - * session. + * Called to create a new connection on top of the given stream. If + * this is a TLS stream, then this function may be used to proxy a + * TLS stream over an HTTP CONNECT session. If this is unset, then + * HTTP CONNECT proxies will not be supported. * - * @param out The created TLS stream + * @param out The created stream * @param in An existing stream to add TLS to * @param host The hostname that the stream is connected to, * for certificate validation @@ -71,17 +72,45 @@ typedef struct { } git_stream_registration; /** - * Register TLS stream constructors for the library to use + * Register stream constructors for the library to use * * If a registration structure is already set, it will be overwritten. * Pass `NULL` in order to deregister the current constructor and return * to the system defaults. * * @param registration the registration data + * @param tls 1 if the registration is for TLS streams, 0 for regular + * (insecure) sockets * @return 0 or an error code */ -GIT_EXTERN(int) git_stream_register_tls( - git_stream_registration *registration); +GIT_EXTERN(int) git_stream_register( + int tls, git_stream_registration *registration); + +/** @name Deprecated TLS Stream Registration Functions + * + * These typedefs and functions are retained for backward compatibility. + * The newer versions of these functions and structures should be preferred + * in all new code. + */ + +/**@{*/ + +/** + * @deprecated Provide a git_stream_registration to git_stream_register + * @see git_stream_registration + */ +typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port); + +/** + * Register a TLS stream constructor for the library to use. This stream + * will not support HTTP CONNECT proxies. + * + * @deprecated Provide a git_stream_registration to git_stream_register + * @see git_stream_register + */ +GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor); + + /**@}*/ GIT_END_DECL diff --git a/src/global.c b/src/global.c index 51d34f61b..86a35a2ff 100644 --- a/src/global.c +++ b/src/global.c @@ -12,7 +12,7 @@ #include "sysdir.h" #include "filter.h" #include "merge_driver.h" -#include "streams/tls.h" +#include "streams/registry.h" #include "streams/mbedtls.h" #include "streams/openssl.h" #include "thread-utils.h" @@ -67,7 +67,7 @@ static int init_common(void) (ret = git_filter_global_init()) == 0 && (ret = git_merge_driver_global_init()) == 0 && (ret = git_transport_ssh_global_init()) == 0 && - (ret = git_tls_stream_global_init()) == 0 && + (ret = git_stream_registry_global_init()) == 0 && (ret = git_openssl_stream_global_init()) == 0 && (ret = git_mbedtls_stream_global_init()) == 0) ret = git_mwindow_global_init(); diff --git a/src/streams/registry.c b/src/streams/registry.c new file mode 100644 index 000000000..210e2d024 --- /dev/null +++ b/src/streams/registry.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "git2/errors.h" + +#include "common.h" +#include "global.h" +#include "streams/tls.h" +#include "streams/mbedtls.h" +#include "streams/openssl.h" +#include "streams/stransport.h" + +struct stream_registry { + git_rwlock lock; + git_stream_registration callbacks; + git_stream_registration tls_callbacks; +}; + +static struct stream_registry stream_registry; + +static void shutdown_stream_registry(void) +{ + git_rwlock_free(&stream_registry.lock); +} + +int git_stream_registry_global_init(void) +{ + if (git_rwlock_init(&stream_registry.lock) < 0) + return -1; + + git__on_shutdown(shutdown_stream_registry); + return 0; +} + +int git_stream_registry_lookup(git_stream_registration *out, int tls) +{ + git_stream_registration *target = tls ? + &stream_registry.callbacks : + &stream_registry.tls_callbacks; + int error = GIT_ENOTFOUND; + + assert(out); + + if (git_rwlock_rdlock(&stream_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock stream registry"); + return -1; + } + + if (target->init) { + memcpy(out, target, sizeof(git_stream_registration)); + error = 0; + } + + git_rwlock_rdunlock(&stream_registry.lock); + return error; +} + +int git_stream_register(int tls, git_stream_registration *registration) +{ + git_stream_registration *target = tls ? + &stream_registry.callbacks : + &stream_registry.tls_callbacks; + + assert(!registration || registration->init); + + GITERR_CHECK_VERSION(registration, GIT_STREAM_VERSION, "stream_registration"); + + if (git_rwlock_wrlock(&stream_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock stream registry"); + return -1; + } + + if (registration) + memcpy(target, registration, sizeof(git_stream_registration)); + else + memset(target, 0, sizeof(git_stream_registration)); + + git_rwlock_wrunlock(&stream_registry.lock); + return 0; +} + +int git_stream_register_tls(git_stream_cb ctor) +{ + git_stream_registration registration = {0}; + + if (ctor) { + registration.version = GIT_STREAM_VERSION; + registration.init = ctor; + registration.wrap = NULL; + + return git_stream_register(1, ®istration); + } else { + return git_stream_register(1, NULL); + } +} diff --git a/src/streams/registry.h b/src/streams/registry.h new file mode 100644 index 000000000..92f87a7bc --- /dev/null +++ b/src/streams/registry.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_streams_registry_h__ +#define INCLUDE_streams_registry_h__ + +#include "common.h" +#include "git2/sys/stream.h" + +/** Configure stream registry. */ +int git_stream_registry_global_init(void); + +/** Lookup a stream registration. */ +extern int git_stream_registry_lookup(git_stream_registration *out, int tls); + +#endif diff --git a/src/streams/socket.c b/src/streams/socket.c index 0c6073b66..21f7fea06 100644 --- a/src/streams/socket.c +++ b/src/streams/socket.c @@ -9,6 +9,7 @@ #include "posix.h" #include "netops.h" +#include "registry.h" #include "stream.h" #ifndef _WIN32 @@ -180,11 +181,14 @@ void socket_free(git_stream *stream) git__free(st); } -int git_socket_stream_new(git_stream **out, const char *host, const char *port) +static int default_socket_stream_new( + git_stream **out, + const char *host, + const char *port) { git_socket_stream *st; - assert(out && host); + assert(out && host && port); st = git__calloc(1, sizeof(git_socket_stream)); GITERR_CHECK_ALLOC(st); @@ -208,3 +212,29 @@ int git_socket_stream_new(git_stream **out, const char *host, const char *port) *out = (git_stream *) st; return 0; } + +int git_socket_stream_new( + git_stream **out, + const char *host, + const char *port) +{ + int (*init)(git_stream **, const char *, const char *) = NULL; + git_stream_registration custom = {0}; + int error; + + assert(out && host && port); + + if ((error = git_stream_registry_lookup(&custom, 0)) == 0) + init = custom.init; + else if (error == GIT_ENOTFOUND) + init = default_socket_stream_new; + else + return error; + + if (!init) { + giterr_set(GITERR_NET, "there is no socket stream available"); + return -1; + } + + return init(out, host, port); +} diff --git a/src/streams/tls.c b/src/streams/tls.c index fe0725272..0e10697cd 100644 --- a/src/streams/tls.c +++ b/src/streams/tls.c @@ -9,66 +9,23 @@ #include "common.h" #include "global.h" +#include "streams/registry.h" #include "streams/tls.h" #include "streams/mbedtls.h" #include "streams/openssl.h" #include "streams/stransport.h" -struct git_tls_stream_registration { - git_rwlock lock; - git_stream_registration callbacks; -}; - -static struct git_tls_stream_registration stream_registration; - -static void shutdown_ssl(void) -{ - git_rwlock_free(&stream_registration.lock); -} - -int git_tls_stream_global_init(void) -{ - if (git_rwlock_init(&stream_registration.lock) < 0) - return -1; - - git__on_shutdown(shutdown_ssl); - return 0; -} - -int git_stream_register_tls(git_stream_registration *registration) -{ - assert(!registration || registration->init); - - if (git_rwlock_wrlock(&stream_registration.lock) < 0) { - giterr_set(GITERR_OS, "failed to lock stream registration"); - return -1; - } - - if (registration) - memcpy(&stream_registration.callbacks, registration, - sizeof(git_stream_registration)); - else - memset(&stream_registration.callbacks, 0, - sizeof(git_stream_registration)); - - git_rwlock_wrunlock(&stream_registration.lock); - return 0; -} - int git_tls_stream_new(git_stream **out, const char *host, const char *port) { int (*init)(git_stream **, const char *, const char *) = NULL; + git_stream_registration custom = {0}; + int error; assert(out && host && port); - if (git_rwlock_rdlock(&stream_registration.lock) < 0) { - giterr_set(GITERR_OS, "failed to lock stream registration"); - return -1; - } - - if (stream_registration.callbacks.init) { - init = stream_registration.callbacks.init; - } else { + if ((error = git_stream_registry_lookup(&custom, 1)) == 0) { + init = custom.init; + } else if (error == GIT_ENOTFOUND) { #ifdef GIT_SECURE_TRANSPORT init = git_stransport_stream_new; #elif defined(GIT_OPENSSL) @@ -76,11 +33,8 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port) #elif defined(GIT_MBEDTLS) init = git_mbedtls_stream_new; #endif - } - - if (git_rwlock_rdunlock(&stream_registration.lock) < 0) { - giterr_set(GITERR_OS, "failed to unlock stream registration"); - return -1; + } else { + return error; } if (!init) { @@ -94,16 +48,12 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port) int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host) { int (*wrap)(git_stream **, git_stream *, const char *) = NULL; + git_stream_registration custom = {0}; assert(out && in); - if (git_rwlock_rdlock(&stream_registration.lock) < 0) { - giterr_set(GITERR_OS, "failed to lock stream registration"); - return -1; - } - - if (stream_registration.callbacks.wrap) { - wrap = stream_registration.callbacks.wrap; + if (git_stream_registry_lookup(&custom, 1) == 0) { + wrap = custom.wrap; } else { #ifdef GIT_SECURE_TRANSPORT wrap = git_stransport_stream_wrap; @@ -114,11 +64,6 @@ int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host) #endif } - if (git_rwlock_rdunlock(&stream_registration.lock) < 0) { - giterr_set(GITERR_OS, "failed to unlock stream registration"); - return -1; - } - if (!wrap) { giterr_set(GITERR_SSL, "there is no TLS stream available"); return -1; diff --git a/src/streams/tls.h b/src/streams/tls.h index 00c6e0b56..465a6ea89 100644 --- a/src/streams/tls.h +++ b/src/streams/tls.h @@ -11,9 +11,6 @@ #include "git2/sys/stream.h" -/** Configure TLS stream functions. */ -int git_tls_stream_global_init(void); - /** * Create a TLS stream with the most appropriate backend available for * the current platform, whether that's SecureTransport on macOS, diff --git a/tests/core/stream.c b/tests/core/stream.c index 872571f39..a76169d48 100644 --- a/tests/core/stream.c +++ b/tests/core/stream.c @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "git2/sys/stream.h" #include "streams/tls.h" +#include "streams/socket.h" #include "stream.h" static git_stream test_stream; @@ -28,6 +29,32 @@ static int test_stream_wrap(git_stream **out, git_stream *in, const char *host) return 0; } +void test_core_stream__register_insecure(void) +{ + git_stream *stream; + git_stream_registration registration = {0}; + + registration.version = 1; + registration.init = test_stream_init; + registration.wrap = test_stream_wrap; + + ctor_called = 0; + cl_git_pass(git_stream_register(0, ®istration)); + cl_git_pass(git_socket_stream_new(&stream, "localhost", "80")); + cl_assert_equal_i(1, ctor_called); + cl_assert_equal_p(&test_stream, stream); + + ctor_called = 0; + stream = NULL; + cl_git_pass(git_stream_register(0, NULL)); + cl_git_pass(git_socket_stream_new(&stream, "localhost", "80")); + + cl_assert_equal_i(0, ctor_called); + cl_assert(&test_stream != stream); + + git_stream_free(stream); +} + void test_core_stream__register_tls(void) { git_stream *stream; @@ -39,14 +66,14 @@ void test_core_stream__register_tls(void) registration.wrap = test_stream_wrap; ctor_called = 0; - cl_git_pass(git_stream_register_tls(®istration)); + cl_git_pass(git_stream_register(1, ®istration)); cl_git_pass(git_tls_stream_new(&stream, "localhost", "443")); cl_assert_equal_i(1, ctor_called); cl_assert_equal_p(&test_stream, stream); ctor_called = 0; stream = NULL; - cl_git_pass(git_stream_register_tls(NULL)); + cl_git_pass(git_stream_register(1, NULL)); error = git_tls_stream_new(&stream, "localhost", "443"); /* We don't have TLS support enabled, or we're on Windows, @@ -63,3 +90,35 @@ void test_core_stream__register_tls(void) git_stream_free(stream); } + +void test_core_stream__register_tls_deprecated(void) +{ + git_stream *stream; + int error; + + ctor_called = 0; + cl_git_pass(git_stream_register_tls(test_stream_init)); + cl_git_pass(git_tls_stream_new(&stream, "localhost", "443")); + cl_assert_equal_i(1, ctor_called); + cl_assert_equal_p(&test_stream, stream); + + ctor_called = 0; + stream = NULL; + cl_git_pass(git_stream_register_tls(NULL)); + error = git_tls_stream_new(&stream, "localhost", "443"); + + /* + * We don't have TLS support enabled, or we're on Windows, + * which has no arbitrary TLS stream support. + */ +#if defined(GIT_WIN32) || !defined(GIT_HTTPS) + cl_git_fail_with(-1, error); +#else + cl_git_pass(error); +#endif + + cl_assert_equal_i(0, ctor_called); + cl_assert(&test_stream != stream); + + git_stream_free(stream); +} |
