1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "common.h"
9 
10 #include "streams/registry.h"
11 
12 #include "runtime.h"
13 #include "streams/tls.h"
14 #include "streams/mbedtls.h"
15 #include "streams/openssl.h"
16 #include "streams/stransport.h"
17 
18 struct stream_registry {
19 	git_rwlock lock;
20 	git_stream_registration callbacks;
21 	git_stream_registration tls_callbacks;
22 };
23 
24 static struct stream_registry stream_registry;
25 
shutdown_stream_registry(void)26 static void shutdown_stream_registry(void)
27 {
28 	git_rwlock_free(&stream_registry.lock);
29 }
30 
git_stream_registry_global_init(void)31 int git_stream_registry_global_init(void)
32 {
33 	if (git_rwlock_init(&stream_registry.lock) < 0)
34 		return -1;
35 
36 	return git_runtime_shutdown_register(shutdown_stream_registry);
37 }
38 
stream_registration_cpy(git_stream_registration * target,git_stream_registration * src)39 GIT_INLINE(void) stream_registration_cpy(
40 	git_stream_registration *target,
41 	git_stream_registration *src)
42 {
43 	if (src)
44 		memcpy(target, src, sizeof(git_stream_registration));
45 	else
46 		memset(target, 0, sizeof(git_stream_registration));
47 }
48 
git_stream_registry_lookup(git_stream_registration * out,git_stream_t type)49 int git_stream_registry_lookup(git_stream_registration *out, git_stream_t type)
50 {
51 	git_stream_registration *target;
52 	int error = GIT_ENOTFOUND;
53 
54 	GIT_ASSERT_ARG(out);
55 
56 	switch(type) {
57 	case GIT_STREAM_STANDARD:
58 		target = &stream_registry.callbacks;
59 		break;
60 	case GIT_STREAM_TLS:
61 		target = &stream_registry.tls_callbacks;
62 		break;
63 	default:
64 		git_error_set(GIT_ERROR_INVALID, "invalid stream type");
65 		return -1;
66 	}
67 
68 	if (git_rwlock_rdlock(&stream_registry.lock) < 0) {
69 		git_error_set(GIT_ERROR_OS, "failed to lock stream registry");
70 		return -1;
71 	}
72 
73 	if (target->init) {
74 		stream_registration_cpy(out, target);
75 		error = 0;
76 	}
77 
78 	git_rwlock_rdunlock(&stream_registry.lock);
79 	return error;
80 }
81 
git_stream_register(git_stream_t type,git_stream_registration * registration)82 int git_stream_register(git_stream_t type, git_stream_registration *registration)
83 {
84 	GIT_ASSERT(!registration || registration->init);
85 
86 	GIT_ERROR_CHECK_VERSION(registration, GIT_STREAM_VERSION, "stream_registration");
87 
88 	if (git_rwlock_wrlock(&stream_registry.lock) < 0) {
89 		git_error_set(GIT_ERROR_OS, "failed to lock stream registry");
90 		return -1;
91 	}
92 
93 	if ((type & GIT_STREAM_STANDARD) == GIT_STREAM_STANDARD)
94 		stream_registration_cpy(&stream_registry.callbacks, registration);
95 
96 	if ((type & GIT_STREAM_TLS) == GIT_STREAM_TLS)
97 		stream_registration_cpy(&stream_registry.tls_callbacks, registration);
98 
99 	git_rwlock_wrunlock(&stream_registry.lock);
100 	return 0;
101 }
102 
103 #ifndef GIT_DEPRECATE_HARD
git_stream_register_tls(int GIT_CALLBACK (ctor)(git_stream ** out,const char * host,const char * port))104 int git_stream_register_tls(
105 	int GIT_CALLBACK(ctor)(git_stream **out, const char *host, const char *port))
106 {
107 	git_stream_registration registration = {0};
108 
109 	if (ctor) {
110 		registration.version = GIT_STREAM_VERSION;
111 		registration.init = ctor;
112 		registration.wrap = NULL;
113 
114 		return git_stream_register(GIT_STREAM_TLS, &registration);
115 	} else {
116 		return git_stream_register(GIT_STREAM_TLS, NULL);
117 	}
118 }
119 #endif
120