Commit df2cc108 by Edward Thomson

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.
parent 0467606f
......@@ -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
......
......@@ -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();
......
/*
* 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);
}
}
/*
* 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
......@@ -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);
}
......@@ -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;
......
......@@ -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,
......
#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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment