summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-11-18 10:29:07 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2018-11-28 15:46:57 +0000
commitdf2cc1087f6de8718319e5bcc65ca8e0e07b717e (patch)
tree02264b646329a5eeca8940fa8557e714e303a50f
parent0467606ff4dbf57401c8b58188652df821ec865b (diff)
downloadlibgit2-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.h47
-rw-r--r--src/global.c4
-rw-r--r--src/streams/registry.c99
-rw-r--r--src/streams/registry.h19
-rw-r--r--src/streams/socket.c34
-rw-r--r--src/streams/tls.c77
-rw-r--r--src/streams/tls.h3
-rw-r--r--tests/core/stream.c63
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, &registration);
+ } 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, &registration));
+ 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(&registration));
+ cl_git_pass(git_stream_register(1, &registration));
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);
+}