mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-03 11:01:01 +00:00
commit
966fbdcb8e
@ -89,6 +89,13 @@ IF (NOT CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||
ENDIF ()
|
||||
|
||||
FIND_PACKAGE(OpenSSL)
|
||||
IF (OPENSSL_FOUND)
|
||||
ADD_DEFINITIONS(-DGIT_SSL)
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
IF (THREADSAFE)
|
||||
IF (NOT WIN32)
|
||||
find_package(Threads REQUIRED)
|
||||
@ -121,7 +128,7 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
TARGET_LINK_LIBRARIES(git2 socket nsl)
|
||||
ENDIF ()
|
||||
|
||||
TARGET_LINK_LIBRARIES(git2 ${CMAKE_THREAD_LIBS_INIT})
|
||||
TARGET_LINK_LIBRARIES(git2 ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES})
|
||||
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
|
||||
SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR})
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
|
||||
@ -157,7 +164,7 @@ IF (BUILD_CLAR)
|
||||
WORKING_DIRECTORY ${CLAR_PATH}
|
||||
)
|
||||
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES})
|
||||
IF (WIN32)
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
|
||||
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
|
@ -88,6 +88,7 @@ typedef enum {
|
||||
GITERR_TAG,
|
||||
GITERR_TREE,
|
||||
GITERR_INDEXER,
|
||||
GITERR_SSL,
|
||||
} git_error_t;
|
||||
|
||||
/**
|
||||
|
@ -229,6 +229,15 @@ GIT_EXTERN(int) git_remote_list(git_strarray *remotes_list, git_repository *repo
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url);
|
||||
|
||||
/**
|
||||
* Choose whether to check the server's certificate (applies to HTTPS only)
|
||||
*
|
||||
* @param remote the remote to configure
|
||||
* @param check whether to check the server's certificate (defaults to yes)
|
||||
*/
|
||||
|
||||
GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -65,7 +65,6 @@ void giterr_clear(void);
|
||||
void giterr_set_str(int error_class, const char *string);
|
||||
void giterr_set_regex(const regex_t *regex, int error_code);
|
||||
|
||||
|
||||
#include "util.h"
|
||||
|
||||
|
||||
|
@ -110,7 +110,7 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
|
||||
int git_fetch__download_pack(
|
||||
const char *buffered,
|
||||
size_t buffered_size,
|
||||
GIT_SOCKET fd,
|
||||
git_transport *t,
|
||||
git_repository *repo,
|
||||
git_off_t *bytes,
|
||||
git_indexer_stats *stats)
|
||||
@ -120,7 +120,7 @@ int git_fetch__download_pack(
|
||||
gitno_buffer buf;
|
||||
git_indexer_stream *idx;
|
||||
|
||||
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
|
||||
gitno_buffer_setup(t, &buf, buff, sizeof(buff));
|
||||
|
||||
if (memcmp(buffered, "PACK", strlen("PACK"))) {
|
||||
giterr_set(GITERR_NET, "The pack doesn't start with the signature");
|
||||
|
@ -12,7 +12,7 @@
|
||||
int git_fetch_negotiate(git_remote *remote);
|
||||
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
|
||||
int git_fetch__download_pack(const char *buffered, size_t buffered_size, git_transport *t,
|
||||
git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
|
||||
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
|
||||
|
||||
|
320
src/netops.c
320
src/netops.c
@ -18,13 +18,20 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef GIT_SSL
|
||||
# include <openssl/ssl.h>
|
||||
# include <openssl/x509v3.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "git2/errors.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "netops.h"
|
||||
#include "posix.h"
|
||||
#include "buffer.h"
|
||||
#include "transport.h"
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
static void net_set_error(const char *str)
|
||||
@ -45,25 +52,68 @@ static void net_set_error(const char *str)
|
||||
}
|
||||
#endif
|
||||
|
||||
void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
|
||||
#ifdef GIT_SSL
|
||||
static int ssl_set_error(gitno_ssl *ssl, int error)
|
||||
{
|
||||
int err;
|
||||
err = SSL_get_error(ssl->ssl, error);
|
||||
giterr_set(GITERR_NET, "SSL error: %s", ERR_error_string(err, NULL));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len)
|
||||
{
|
||||
memset(buf, 0x0, sizeof(gitno_buffer));
|
||||
memset(data, 0x0, len);
|
||||
buf->data = data;
|
||||
buf->len = len;
|
||||
buf->offset = 0;
|
||||
buf->fd = fd;
|
||||
buf->fd = t->socket;
|
||||
#ifdef GIT_SSL
|
||||
if (t->encrypt)
|
||||
buf->ssl = &t->ssl;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GIT_SSL
|
||||
static int ssl_recv(gitno_ssl *ssl, void *data, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = SSL_read(ssl->ssl, data, len);
|
||||
} while (SSL_get_error(ssl->ssl, ret) == SSL_ERROR_WANT_READ);
|
||||
|
||||
if (ret < 0)
|
||||
return ssl_set_error(ssl, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int gitno_recv(gitno_buffer *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef GIT_SSL
|
||||
if (buf->ssl != NULL) {
|
||||
if ((ret = ssl_recv(buf->ssl, buf->data + buf->offset, buf->len - buf->offset)) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
|
||||
if (ret < 0) {
|
||||
net_set_error("Error receiving socket data");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
|
||||
if (ret < 0) {
|
||||
net_set_error("Error receiving socket data");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
buf->offset += ret;
|
||||
return ret;
|
||||
@ -92,7 +142,237 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons)
|
||||
buf->offset -= cons;
|
||||
}
|
||||
|
||||
int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
|
||||
int gitno_ssl_teardown(git_transport *t)
|
||||
{
|
||||
int ret = ret;
|
||||
|
||||
if (!t->encrypt)
|
||||
return 0;
|
||||
|
||||
#ifdef GIT_SSL
|
||||
|
||||
do {
|
||||
ret = SSL_shutdown(t->ssl.ssl);
|
||||
} while (ret == 0);
|
||||
if (ret < 0)
|
||||
return ssl_set_error(&t->ssl, ret);
|
||||
|
||||
SSL_free(t->ssl.ssl);
|
||||
SSL_CTX_free(t->ssl.ctx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef GIT_SSL
|
||||
/* Match host names according to RFC 2818 rules */
|
||||
static int match_host(const char *pattern, const char *host)
|
||||
{
|
||||
for (;;) {
|
||||
char c = tolower(*pattern++);
|
||||
|
||||
if (c == '\0')
|
||||
return *host ? -1 : 0;
|
||||
|
||||
if (c == '*') {
|
||||
c = *pattern;
|
||||
/* '*' at the end matches everything left */
|
||||
if (c == '\0')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We've found a pattern, so move towards the next matching
|
||||
* char. The '.' is handled specially because wildcards aren't
|
||||
* allowed to cross subdomains.
|
||||
*/
|
||||
|
||||
while(*host) {
|
||||
char h = tolower(*host);
|
||||
if (c == h)
|
||||
return match_host(pattern, host++);
|
||||
if (h == '.')
|
||||
return match_host(pattern, host);
|
||||
host++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c != tolower(*host++))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_host_name(const char *name, const char *host)
|
||||
{
|
||||
if (!strcasecmp(name, host))
|
||||
return 0;
|
||||
|
||||
if (match_host(name, host) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_server_cert(git_transport *t, const char *host)
|
||||
{
|
||||
X509 *cert;
|
||||
X509_NAME *peer_name;
|
||||
ASN1_STRING *str;
|
||||
unsigned char *peer_cn = NULL;
|
||||
int matched = -1, type = GEN_DNS;
|
||||
GENERAL_NAMES *alts;
|
||||
struct in6_addr addr6;
|
||||
struct in_addr addr4;
|
||||
void *addr;
|
||||
int i = -1,j;
|
||||
|
||||
|
||||
/* Try to parse the host as an IP address to see if it is */
|
||||
if (inet_pton(AF_INET, host, &addr4)) {
|
||||
type = GEN_IPADD;
|
||||
addr = &addr4;
|
||||
} else {
|
||||
if(inet_pton(AF_INET6, host, &addr6)) {
|
||||
type = GEN_IPADD;
|
||||
addr = &addr6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cert = SSL_get_peer_certificate(t->ssl.ssl);
|
||||
|
||||
/* Check the alternative names */
|
||||
alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
||||
if (alts) {
|
||||
int num;
|
||||
|
||||
num = sk_GENERAL_NAME_num(alts);
|
||||
for (i = 0; i < num && matched != 1; i++) {
|
||||
const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
|
||||
const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
|
||||
size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
|
||||
|
||||
/* Skip any names of a type we're not looking for */
|
||||
if (gn->type != type)
|
||||
continue;
|
||||
|
||||
if (type == GEN_DNS) {
|
||||
/* If it contains embedded NULs, don't even try */
|
||||
if (memchr(name, '\0', namelen))
|
||||
continue;
|
||||
|
||||
if (check_host_name(name, host) < 0)
|
||||
matched = 0;
|
||||
else
|
||||
matched = 1;
|
||||
} else if (type == GEN_IPADD) {
|
||||
/* Here name isn't so much a name but a binary representation of the IP */
|
||||
matched = !!memcmp(name, addr, namelen);
|
||||
}
|
||||
}
|
||||
}
|
||||
GENERAL_NAMES_free(alts);
|
||||
|
||||
if (matched == 0)
|
||||
goto on_error;
|
||||
|
||||
if (matched == 1)
|
||||
return 0;
|
||||
|
||||
/* If no alternative names are available, check the common name */
|
||||
peer_name = X509_get_subject_name(cert);
|
||||
if (peer_name == NULL)
|
||||
goto on_error;
|
||||
|
||||
if (peer_name) {
|
||||
/* Get the index of the last CN entry */
|
||||
while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
|
||||
i = j;
|
||||
}
|
||||
|
||||
if (i < 0)
|
||||
goto on_error;
|
||||
|
||||
str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
|
||||
if (str == NULL)
|
||||
goto on_error;
|
||||
|
||||
/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
|
||||
if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
|
||||
int size = ASN1_STRING_length(str);
|
||||
|
||||
if (size > 0) {
|
||||
peer_cn = OPENSSL_malloc(size + 1);
|
||||
GITERR_CHECK_ALLOC(peer_cn);
|
||||
memcpy(peer_cn, ASN1_STRING_data(str), size);
|
||||
peer_cn[size] = '\0';
|
||||
}
|
||||
} else {
|
||||
int size = ASN1_STRING_to_UTF8(&peer_cn, str);
|
||||
GITERR_CHECK_ALLOC(peer_cn);
|
||||
if (memchr(peer_cn, '\0', size))
|
||||
goto cert_fail;
|
||||
}
|
||||
|
||||
if (check_host_name((char *)peer_cn, host) < 0)
|
||||
goto cert_fail;
|
||||
|
||||
OPENSSL_free(peer_cn);
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
OPENSSL_free(peer_cn);
|
||||
return ssl_set_error(&t->ssl, 0);
|
||||
|
||||
cert_fail:
|
||||
OPENSSL_free(peer_cn);
|
||||
giterr_set(GITERR_SSL, "Certificate host name check failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ssl_setup(git_transport *t, const char *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
t->ssl.ctx = SSL_CTX_new(SSLv23_method());
|
||||
if (t->ssl.ctx == NULL)
|
||||
return ssl_set_error(&t->ssl, 0);
|
||||
|
||||
SSL_CTX_set_mode(t->ssl.ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_verify(t->ssl.ctx, SSL_VERIFY_PEER, NULL);
|
||||
if (!SSL_CTX_set_default_verify_paths(t->ssl.ctx))
|
||||
return ssl_set_error(&t->ssl, 0);
|
||||
|
||||
t->ssl.ssl = SSL_new(t->ssl.ctx);
|
||||
if (t->ssl.ssl == NULL)
|
||||
return ssl_set_error(&t->ssl, 0);
|
||||
|
||||
if((ret = SSL_set_fd(t->ssl.ssl, t->socket)) == 0)
|
||||
return ssl_set_error(&t->ssl, ret);
|
||||
|
||||
if ((ret = SSL_connect(t->ssl.ssl)) <= 0)
|
||||
return ssl_set_error(&t->ssl, ret);
|
||||
|
||||
if (t->check_cert && verify_server_cert(t, host) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ssl_setup(git_transport *t, const char *host)
|
||||
{
|
||||
GIT_UNUSED(t);
|
||||
GIT_UNUSED(host);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int gitno_connect(git_transport *t, const char *host, const char *port)
|
||||
{
|
||||
struct addrinfo *info = NULL, *p;
|
||||
struct addrinfo hints;
|
||||
@ -129,20 +409,46 @@ int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
|
||||
return -1;
|
||||
}
|
||||
|
||||
t->socket = s;
|
||||
freeaddrinfo(info);
|
||||
*sock = s;
|
||||
|
||||
if (t->encrypt && ssl_setup(t, host) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
|
||||
#ifdef GIT_SSL
|
||||
static int send_ssl(gitno_ssl *ssl, const char *msg, size_t len)
|
||||
{
|
||||
int ret;
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
errno = 0;
|
||||
ret = SSL_write(ssl->ssl, msg + off, len - off);
|
||||
if (ret <= 0)
|
||||
return ssl_set_error(ssl, ret);
|
||||
|
||||
ret = p_send(s, msg + off, len - off, flags);
|
||||
off += ret;
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
#endif
|
||||
|
||||
int gitno_send(git_transport *t, const char *msg, size_t len, int flags)
|
||||
{
|
||||
int ret;
|
||||
size_t off = 0;
|
||||
|
||||
#ifdef GIT_SSL
|
||||
if (t->encrypt)
|
||||
return send_ssl(&t->ssl, msg, len);
|
||||
#endif
|
||||
|
||||
while (off < len) {
|
||||
errno = 0;
|
||||
ret = p_send(t->socket, msg + off, len - off, flags);
|
||||
if (ret < 0) {
|
||||
net_set_error("Error sending data");
|
||||
return -1;
|
||||
|
13
src/netops.h
13
src/netops.h
@ -8,22 +8,29 @@
|
||||
#define INCLUDE_netops_h__
|
||||
|
||||
#include "posix.h"
|
||||
#include "transport.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef struct gitno_buffer {
|
||||
char *data;
|
||||
size_t len;
|
||||
size_t offset;
|
||||
GIT_SOCKET fd;
|
||||
#ifdef GIT_SSL
|
||||
struct gitno_ssl *ssl;
|
||||
#endif
|
||||
} gitno_buffer;
|
||||
|
||||
void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
|
||||
void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len);
|
||||
int gitno_recv(gitno_buffer *buf);
|
||||
|
||||
void gitno_consume(gitno_buffer *buf, const char *ptr);
|
||||
void gitno_consume_n(gitno_buffer *buf, size_t cons);
|
||||
|
||||
int gitno_connect(GIT_SOCKET *s, const char *host, const char *port);
|
||||
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);
|
||||
int gitno_connect(git_transport *t, const char *host, const char *port);
|
||||
int gitno_send(git_transport *t, const char *msg, size_t len, int flags);
|
||||
int gitno_close(GIT_SOCKET s);
|
||||
int gitno_ssl_teardown(git_transport *t);
|
||||
int gitno_send_chunk_size(int s, size_t len);
|
||||
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
|
||||
|
||||
|
@ -281,12 +281,6 @@ int git_pkt_buffer_flush(git_buf *buf)
|
||||
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
|
||||
}
|
||||
|
||||
int git_pkt_send_flush(GIT_SOCKET s)
|
||||
{
|
||||
|
||||
return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);
|
||||
}
|
||||
|
||||
static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf)
|
||||
{
|
||||
char capstr[20];
|
||||
|
10
src/remote.c
10
src/remote.c
@ -66,6 +66,7 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *name, con
|
||||
|
||||
memset(remote, 0x0, sizeof(git_remote));
|
||||
remote->repo = repo;
|
||||
remote->check_cert = 1;
|
||||
|
||||
if (git_vector_init(&remote->refs, 32, NULL) < 0)
|
||||
return -1;
|
||||
@ -108,6 +109,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
GITERR_CHECK_ALLOC(remote);
|
||||
|
||||
memset(remote, 0x0, sizeof(git_remote));
|
||||
remote->check_cert = 1;
|
||||
remote->name = git__strdup(name);
|
||||
GITERR_CHECK_ALLOC(remote->name);
|
||||
|
||||
@ -291,6 +293,7 @@ int git_remote_connect(git_remote *remote, int direction)
|
||||
if (git_transport_new(&t, remote->url) < 0)
|
||||
return -1;
|
||||
|
||||
t->check_cert = remote->check_cert;
|
||||
if (t->connect(t, direction) < 0) {
|
||||
goto on_error;
|
||||
}
|
||||
@ -512,3 +515,10 @@ on_error:
|
||||
git_remote_free(*out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void git_remote_check_cert(git_remote *remote, int check)
|
||||
{
|
||||
assert(remote);
|
||||
|
||||
remote->check_cert = check;
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ struct git_remote {
|
||||
struct git_refspec push;
|
||||
git_transport *transport;
|
||||
git_repository *repo;
|
||||
unsigned int need_pack:1;
|
||||
unsigned int need_pack:1,
|
||||
check_cert;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@ static struct {
|
||||
} transports[] = {
|
||||
{"git://", git_transport_git},
|
||||
{"http://", git_transport_http},
|
||||
{"https://", git_transport_dummy},
|
||||
{"https://", git_transport_https},
|
||||
{"file://", git_transport_local},
|
||||
{"git+ssh://", git_transport_dummy},
|
||||
{"ssh+git://", git_transport_dummy},
|
||||
|
@ -10,6 +10,13 @@
|
||||
#include "git2/net.h"
|
||||
#include "git2/indexer.h"
|
||||
#include "vector.h"
|
||||
#include "posix.h"
|
||||
#include "common.h"
|
||||
#ifdef GIT_SSL
|
||||
# include <openssl/ssl.h>
|
||||
# include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define GIT_CAP_OFS_DELTA "ofs-delta"
|
||||
|
||||
@ -18,6 +25,14 @@ typedef struct git_transport_caps {
|
||||
ofs_delta:1;
|
||||
} git_transport_caps;
|
||||
|
||||
#ifdef GIT_SSL
|
||||
typedef struct gitno_ssl {
|
||||
SSL_CTX *ctx;
|
||||
SSL *ssl;
|
||||
} gitno_ssl;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* A day in the life of a network operation
|
||||
* ========================================
|
||||
@ -53,7 +68,13 @@ struct git_transport {
|
||||
* Whether we want to push or fetch
|
||||
*/
|
||||
int direction : 1, /* 0 fetch, 1 push */
|
||||
connected : 1;
|
||||
connected : 1,
|
||||
check_cert: 1,
|
||||
encrypt : 1;
|
||||
#ifdef GIT_SSL
|
||||
struct gitno_ssl ssl;
|
||||
#endif
|
||||
GIT_SOCKET socket;
|
||||
/**
|
||||
* Connect and store the remote heads
|
||||
*/
|
||||
@ -94,6 +115,7 @@ int git_transport_new(struct git_transport **transport, const char *url);
|
||||
int git_transport_local(struct git_transport **transport);
|
||||
int git_transport_git(struct git_transport **transport);
|
||||
int git_transport_http(struct git_transport **transport);
|
||||
int git_transport_https(struct git_transport **transport);
|
||||
int git_transport_dummy(struct git_transport **transport);
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,6 @@
|
||||
typedef struct {
|
||||
git_transport parent;
|
||||
git_protocol proto;
|
||||
GIT_SOCKET socket;
|
||||
git_vector refs;
|
||||
git_remote_head **heads;
|
||||
git_transport_caps caps;
|
||||
@ -77,7 +76,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
|
||||
static int send_request(git_transport *t, const char *cmd, const char *url)
|
||||
{
|
||||
int error;
|
||||
git_buf request = GIT_BUF_INIT;
|
||||
@ -86,7 +85,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = gitno_send(s, request.ptr, request.size, 0);
|
||||
error = gitno_send(t, request.ptr, request.size, 0);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&request);
|
||||
@ -102,9 +101,6 @@ static int do_connect(transport_git *t, const char *url)
|
||||
{
|
||||
char *host, *port;
|
||||
const char prefix[] = "git://";
|
||||
int error;
|
||||
|
||||
t->socket = INVALID_SOCKET;
|
||||
|
||||
if (!git__prefixcmp(url, prefix))
|
||||
url += strlen(prefix);
|
||||
@ -112,24 +108,22 @@ static int do_connect(transport_git *t, const char *url)
|
||||
if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = gitno_connect(&t->socket, host, port)) == 0) {
|
||||
error = send_request(t->socket, NULL, url);
|
||||
}
|
||||
if (gitno_connect((git_transport *)t, host, port) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (send_request((git_transport *)t, NULL, url) < 0)
|
||||
goto on_error;
|
||||
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
|
||||
if (error < 0 && t->socket != INVALID_SOCKET) {
|
||||
gitno_close(t->socket);
|
||||
t->socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (t->socket == INVALID_SOCKET) {
|
||||
giterr_set(GITERR_NET, "Failed to connect to the host");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
gitno_close(t->parent.socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -215,7 +209,7 @@ static int git_connect(git_transport *transport, int direction)
|
||||
if (do_connect(t, transport->url) < 0)
|
||||
goto cleanup;
|
||||
|
||||
gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
|
||||
gitno_buffer_setup(transport, &t->buf, t->buff, sizeof(t->buff));
|
||||
|
||||
t->parent.connected = 1;
|
||||
if (store_refs(t) < 0)
|
||||
@ -308,7 +302,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
|
||||
if (git_fetch_setup_walk(&walk, repo) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
|
||||
if (gitno_send(transport, data.ptr, data.size, 0) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_buf_clear(&data);
|
||||
@ -328,7 +322,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
|
||||
if (git_buf_oom(&data))
|
||||
goto on_error;
|
||||
|
||||
if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
|
||||
if (gitno_send(transport, data.ptr, data.size, 0) < 0)
|
||||
goto on_error;
|
||||
|
||||
pkt_type = recv_pkt(buf);
|
||||
@ -351,7 +345,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, c
|
||||
git_buf_clear(&data);
|
||||
git_pkt_buffer_flush(&data);
|
||||
git_pkt_buffer_done(&data);
|
||||
if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
|
||||
if (gitno_send(transport, data.ptr, data.size, 0) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_buf_free(&data);
|
||||
@ -392,7 +386,7 @@ static int git_download_pack(git_transport *transport, git_repository *repo, git
|
||||
|
||||
if (pkt->type == GIT_PKT_PACK) {
|
||||
git__free(pkt);
|
||||
return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
|
||||
return git_fetch__download_pack(buf->data, buf->offset, transport, repo, bytes, stats);
|
||||
}
|
||||
|
||||
/* For now we don't care about anything */
|
||||
@ -406,12 +400,15 @@ static int git_download_pack(git_transport *transport, git_repository *repo, git
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
static int git_close(git_transport *transport)
|
||||
static int git_close(git_transport *t)
|
||||
{
|
||||
transport_git *t = (transport_git*) transport;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
if (git_pkt_buffer_flush(&buf) < 0)
|
||||
return -1;
|
||||
/* Can't do anything if there's an error, so don't bother checking */
|
||||
git_pkt_send_flush(t->socket);
|
||||
gitno_send(t, buf.ptr, buf.size, 0);
|
||||
|
||||
if (gitno_close(t->socket) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to close socket");
|
||||
return -1;
|
||||
|
@ -32,7 +32,6 @@ typedef struct {
|
||||
git_protocol proto;
|
||||
git_vector refs;
|
||||
git_vector common;
|
||||
GIT_SOCKET socket;
|
||||
git_buf buf;
|
||||
git_remote_head **heads;
|
||||
int error;
|
||||
@ -43,6 +42,7 @@ typedef struct {
|
||||
enum last_cb last_cb;
|
||||
http_parser parser;
|
||||
char *content_type;
|
||||
char *path;
|
||||
char *host;
|
||||
char *port;
|
||||
char *service;
|
||||
@ -52,12 +52,9 @@ typedef struct {
|
||||
#endif
|
||||
} transport_http;
|
||||
|
||||
static int gen_request(git_buf *buf, const char *url, const char *host, const char *op,
|
||||
static int gen_request(git_buf *buf, const char *path, const char *host, const char *op,
|
||||
const char *service, ssize_t content_length, int ls)
|
||||
{
|
||||
const char *path = url;
|
||||
|
||||
path = strchr(path, '/');
|
||||
if (path == NULL) /* Is 'git fetch http://host.com/' valid? */
|
||||
path = "/";
|
||||
|
||||
@ -85,15 +82,12 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
|
||||
|
||||
static int do_connect(transport_http *t, const char *host, const char *port)
|
||||
{
|
||||
GIT_SOCKET s;
|
||||
|
||||
if (t->parent.connected && http_should_keep_alive(&t->parser))
|
||||
return 0;
|
||||
|
||||
if (gitno_connect(&s, host, port) < 0)
|
||||
if (gitno_connect((git_transport *) t, host, port) < 0)
|
||||
return -1;
|
||||
|
||||
t->socket = s;
|
||||
t->parent.connected = 1;
|
||||
|
||||
return 0;
|
||||
@ -231,7 +225,7 @@ static int store_refs(transport_http *t)
|
||||
settings.on_body = on_body_store_refs;
|
||||
settings.on_message_complete = on_message_complete;
|
||||
|
||||
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
|
||||
gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer));
|
||||
|
||||
while(1) {
|
||||
size_t parsed;
|
||||
@ -267,7 +261,8 @@ static int http_connect(git_transport *transport, int direction)
|
||||
int ret;
|
||||
git_buf request = GIT_BUF_INIT;
|
||||
const char *service = "upload-pack";
|
||||
const char *url = t->parent.url, *prefix = "http://";
|
||||
const char *url = t->parent.url, *prefix_http = "http://", *prefix_https = "https://";
|
||||
const char *default_port;
|
||||
|
||||
if (direction == GIT_DIR_PUSH) {
|
||||
giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
|
||||
@ -278,10 +273,19 @@ static int http_connect(git_transport *transport, int direction)
|
||||
if (git_vector_init(&t->refs, 16, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git__prefixcmp(url, prefix))
|
||||
url += strlen(prefix);
|
||||
if (!git__prefixcmp(url, prefix_http)) {
|
||||
url = t->parent.url + strlen(prefix_http);
|
||||
default_port = "80";
|
||||
}
|
||||
|
||||
if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)
|
||||
if (!git__prefixcmp(url, prefix_https)) {
|
||||
url += strlen(prefix_https);
|
||||
default_port = "443";
|
||||
}
|
||||
|
||||
t->path = strchr(url, '/');
|
||||
|
||||
if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, default_port)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
t->service = git__strdup(service);
|
||||
@ -291,12 +295,13 @@ static int http_connect(git_transport *transport, int direction)
|
||||
goto cleanup;
|
||||
|
||||
/* Generate and send the HTTP request */
|
||||
if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {
|
||||
if ((ret = gen_request(&request, t->path, t->host, "GET", service, 0, 1)) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to generate request");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
|
||||
|
||||
if (gitno_send(transport, request.ptr, request.size, 0) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = store_refs(t);
|
||||
@ -403,7 +408,7 @@ static int parse_response(transport_http *t)
|
||||
settings.on_body = on_body_parse_response;
|
||||
settings.on_message_complete = on_message_complete;
|
||||
|
||||
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
|
||||
gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer));
|
||||
|
||||
while(1) {
|
||||
size_t parsed;
|
||||
@ -437,13 +442,9 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
|
||||
git_oid oid;
|
||||
git_pkt_ack *pkt;
|
||||
git_vector *common = &t->common;
|
||||
const char *prefix = "http://", *url = t->parent.url;
|
||||
git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT;
|
||||
gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);
|
||||
|
||||
/* TODO: Store url in the transport */
|
||||
if (!git__prefixcmp(url, prefix))
|
||||
url += strlen(prefix);
|
||||
gitno_buffer_setup(transport, &buf, buff, sizeof(buff));
|
||||
|
||||
if (git_vector_init(common, 16, NULL) < 0)
|
||||
return -1;
|
||||
@ -474,13 +475,13 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
|
||||
|
||||
git_pkt_buffer_done(&data);
|
||||
|
||||
if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)
|
||||
if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", data.size, 0)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
|
||||
if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)
|
||||
if ((ret = gitno_send(transport, data.ptr, data.size, 0)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_buf_clear(&request);
|
||||
@ -547,7 +548,7 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
|
||||
git_indexer_stream *idx = NULL;
|
||||
download_pack_cbdata data;
|
||||
|
||||
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
|
||||
gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer));
|
||||
|
||||
if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
|
||||
giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
|
||||
@ -557,7 +558,6 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
|
||||
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
/*
|
||||
* This is part of the previous response, so we don't want to
|
||||
* re-init the parser, just set these two callbacks.
|
||||
@ -576,6 +576,8 @@ static int http_download_pack(git_transport *transport, git_repository *repo, gi
|
||||
if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer));
|
||||
|
||||
do {
|
||||
size_t parsed;
|
||||
|
||||
@ -603,9 +605,10 @@ on_error:
|
||||
|
||||
static int http_close(git_transport *transport)
|
||||
{
|
||||
transport_http *t = (transport_http *) transport;
|
||||
if (gitno_ssl_teardown(transport) < 0)
|
||||
return -1;
|
||||
|
||||
if (gitno_close(t->socket) < 0) {
|
||||
if (gitno_close(transport->socket) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
@ -682,3 +685,23 @@ int git_transport_http(git_transport **out)
|
||||
*out = (git_transport *) t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_transport_https(git_transport **out)
|
||||
{
|
||||
#ifdef GIT_SSL
|
||||
transport_http *t;
|
||||
if (git_transport_http((git_transport **)&t) < 0)
|
||||
return -1;
|
||||
|
||||
t->parent.encrypt = 1;
|
||||
t->parent.check_cert = 1;
|
||||
*out = (git_transport *) t;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
GIT_UNUSED(out);
|
||||
|
||||
giterr_set(GITERR_NET, "HTTPS support not available");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user