diff options
-rw-r--r-- | include/git2/sys/stream.h | 40 | ||||
-rw-r--r-- | src/netops.c | 492 | ||||
-rw-r--r-- | src/netops.h | 11 | ||||
-rw-r--r-- | src/openssl_stream.c | 375 | ||||
-rw-r--r-- | src/openssl_stream.h | 14 | ||||
-rw-r--r-- | src/socket_stream.c | 212 | ||||
-rw-r--r-- | src/socket_stream.h | 21 | ||||
-rw-r--r-- | src/stream.h | 48 | ||||
-rw-r--r-- | src/transports/git.c | 128 | ||||
-rw-r--r-- | src/transports/http.c | 102 | ||||
-rw-r--r-- | src/transports/smart.c | 6 | ||||
-rw-r--r-- | src/transports/ssh.c | 20 |
12 files changed, 857 insertions, 612 deletions
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h new file mode 100644 index 000000000..c249055c9 --- /dev/null +++ b/include/git2/sys/stream.h @@ -0,0 +1,40 @@ +/* + * 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_sys_git_stream_h__ +#define INCLUDE_sys_git_stream_h__ + +#include "git2/common.h" +#include "git2/types.h" + +GIT_BEGIN_DECL + +#define GIT_STREAM_VERSION 1 + +/** + * Every stream must have this struct as its first element, so the + * API can talk to it. You'd define your stream as + * + * struct my_stream { + * git_stream parent; + * ... + * } + * + * and fill the functions + */ +typedef struct git_stream { + int version; + + int encrypted; + int (*connect)(struct git_stream *); + int (*certificate)(git_cert **, struct git_stream *); + ssize_t (*read)(struct git_stream *, void *, size_t); + ssize_t (*write)(struct git_stream *, const char *, size_t, int); + int (*close)(struct git_stream *); + void (*free)(struct git_stream *); +} git_stream; + +#endif diff --git a/src/netops.c b/src/netops.c index 23e7e9d3c..6047cf1ac 100644 --- a/src/netops.c +++ b/src/netops.c @@ -4,27 +4,6 @@ * 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 _WIN32 -# include <sys/types.h> -# include <sys/socket.h> -# include <sys/select.h> -# include <sys/time.h> -# include <netdb.h> -# include <netinet/in.h> -# include <arpa/inet.h> -#else -# include <winsock2.h> -# include <ws2tcpip.h> -# ifdef _MSC_VER -# pragma comment(lib, "ws2_32") -# endif -#endif - -#ifdef GIT_SSL -# include <openssl/ssl.h> -# include <openssl/err.h> -# include <openssl/x509v3.h> -#endif #include <ctype.h> #include "git2/errors.h" @@ -36,136 +15,46 @@ #include "http_parser.h" #include "global.h" -#ifdef GIT_WIN32 -static void net_set_error(const char *str) -{ - int error = WSAGetLastError(); - char * win32_error = git_win32_get_error_message(error); - - if (win32_error) { - giterr_set(GITERR_NET, "%s: %s", str, win32_error); - git__free(win32_error); - } else { - giterr_set(GITERR_NET, str); - } -} -#else -static void net_set_error(const char *str) -{ - giterr_set(GITERR_NET, "%s: %s", str, strerror(errno)); -} -#endif - -#ifdef GIT_SSL -static int ssl_set_error(gitno_ssl *ssl, int error) -{ - int err; - unsigned long e; - - err = SSL_get_error(ssl->ssl, error); - - assert(err != SSL_ERROR_WANT_READ); - assert(err != SSL_ERROR_WANT_WRITE); - - switch (err) { - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - giterr_set(GITERR_NET, "SSL error: connection failure\n"); - break; - case SSL_ERROR_WANT_X509_LOOKUP: - giterr_set(GITERR_NET, "SSL error: x509 error\n"); - break; - case SSL_ERROR_SYSCALL: - e = ERR_get_error(); - if (e > 0) { - giterr_set(GITERR_NET, "SSL error: %s", - ERR_error_string(e, NULL)); - break; - } else if (error < 0) { - giterr_set(GITERR_OS, "SSL error: syscall failure"); - break; - } - giterr_set(GITERR_NET, "SSL error: received early EOF"); - break; - case SSL_ERROR_SSL: - e = ERR_get_error(); - giterr_set(GITERR_NET, "SSL error: %s", - ERR_error_string(e, NULL)); - break; - case SSL_ERROR_NONE: - case SSL_ERROR_ZERO_RETURN: - default: - giterr_set(GITERR_NET, "SSL error: unknown error"); - break; - } - return -1; -} -#endif - int gitno_recv(gitno_buffer *buf) { return buf->recv(buf); } -#ifdef GIT_SSL -static int gitno__recv_ssl(gitno_buffer *buf) +void gitno_buffer_setup_callback( + gitno_buffer *buf, + char *data, + size_t len, + int (*recv)(gitno_buffer *buf), void *cb_data) { - int ret; - - do { - ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset); - } while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ); - - if (ret < 0) { - net_set_error("Error receiving socket data"); - return -1; - } - - buf->offset += ret; - return ret; + memset(data, 0x0, len); + buf->data = data; + buf->len = len; + buf->offset = 0; + buf->recv = recv; + buf->cb_data = cb_data; } -#endif -static int gitno__recv(gitno_buffer *buf) +static int recv_stream(gitno_buffer *buf) { + git_stream *io = (git_stream *) buf->cb_data; int ret; - ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0); - if (ret < 0) { - net_set_error("Error receiving socket data"); + ret = git_stream_read(io, buf->data + buf->offset, buf->len - buf->offset); + if (ret < 0) return -1; - } buf->offset += ret; return ret; } -void gitno_buffer_setup_callback( - gitno_socket *socket, - gitno_buffer *buf, - char *data, - size_t len, - int (*recv)(gitno_buffer *buf), void *cb_data) +void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len) { memset(data, 0x0, len); buf->data = data; buf->len = len; buf->offset = 0; - buf->socket = socket; - buf->recv = recv; - buf->cb_data = cb_data; -} - -void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len) -{ -#ifdef GIT_SSL - if (socket->ssl.ssl) { - gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL); - return; - } -#endif - - gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL); + buf->recv = recv_stream; + buf->cb_data = st; } /* Consume up to ptr and move the rest of the buffer to the beginning */ @@ -191,24 +80,6 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons) buf->offset -= cons; } -#ifdef GIT_SSL - -static int gitno_ssl_teardown(gitno_ssl *ssl) -{ - int ret; - - ret = SSL_shutdown(ssl->ssl); - if (ret < 0) - ret = ssl_set_error(ssl, ret); - else - ret = 0; - - SSL_free(ssl->ssl); - return ret; -} - -#endif - /* Match host names according to RFC 2818 rules */ int gitno__match_host(const char *pattern, const char *host) { @@ -248,333 +119,6 @@ int gitno__match_host(const char *pattern, const char *host) return -1; } -static int check_host_name(const char *name, const char *host) -{ - if (!strcasecmp(name, host)) - return 0; - - if (gitno__match_host(name, host) < 0) - return -1; - - return 0; -} - -#ifdef GIT_SSL - -static int verify_server_cert(gitno_ssl *ssl, 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; - - if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) { - giterr_set(GITERR_SSL, "The SSL certificate is invalid"); - return GIT_ECERTIFICATE; - } - - /* Try to parse the host as an IP address to see if it is */ - if (p_inet_pton(AF_INET, host, &addr4)) { - type = GEN_IPADD; - addr = &addr4; - } else { - if(p_inet_pton(AF_INET6, host, &addr6)) { - type = GEN_IPADD; - addr = &addr6; - } - } - - - cert = SSL_get_peer_certificate(ssl->ssl); - if (!cert) { - giterr_set(GITERR_SSL, "the server did not provide a certificate"); - return -1; - } - - /* 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 cert_fail_name; - - 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_name; - } - - if (check_host_name((char *)peer_cn, host) < 0) - goto cert_fail_name; - - OPENSSL_free(peer_cn); - - return 0; - -on_error: - OPENSSL_free(peer_cn); - return ssl_set_error(ssl, 0); - -cert_fail_name: - OPENSSL_free(peer_cn); - giterr_set(GITERR_SSL, "hostname does not match certificate"); - return GIT_ECERTIFICATE; -} - -static int ssl_setup(gitno_socket *socket, const char *host) -{ - int ret; - - if (git__ssl_ctx == NULL) { - giterr_set(GITERR_NET, "OpenSSL initialization failed"); - return -1; - } - - socket->ssl.ssl = SSL_new(git__ssl_ctx); - if (socket->ssl.ssl == NULL) - return ssl_set_error(&socket->ssl, 0); - - if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0) - return ssl_set_error(&socket->ssl, ret); - - if ((ret = SSL_connect(socket->ssl.ssl)) <= 0) - return ssl_set_error(&socket->ssl, ret); - - return verify_server_cert(&socket->ssl, host); -} -#endif - -static int gitno__close(GIT_SOCKET s) -{ -#ifdef GIT_WIN32 - if (SOCKET_ERROR == closesocket(s)) - return -1; - - if (0 != WSACleanup()) { - giterr_set(GITERR_OS, "Winsock cleanup failed"); - return -1; - } - - return 0; -#else - return close(s); -#endif -} - -int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags) -{ - struct addrinfo *info = NULL, *p; - struct addrinfo hints; - GIT_SOCKET s = INVALID_SOCKET; - int ret; - -#ifdef GIT_WIN32 - /* on win32, the WSA context needs to be initialized - * before any socket calls can be performed */ - WSADATA wsd; - - if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { - giterr_set(GITERR_OS, "Winsock init failed"); - return -1; - } - - if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { - WSACleanup(); - giterr_set(GITERR_OS, "Winsock init failed"); - return -1; - } -#endif - - /* Zero the socket structure provided */ - memset(s_out, 0x0, sizeof(gitno_socket)); - - memset(&hints, 0x0, sizeof(struct addrinfo)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = AF_UNSPEC; - - if ((ret = p_getaddrinfo(host, port, &hints, &info)) != 0) { - giterr_set(GITERR_NET, - "Failed to resolve address for %s: %s", host, p_gai_strerror(ret)); - return -1; - } - - for (p = info; p != NULL; p = p->ai_next) { - s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - - if (s == INVALID_SOCKET) { - net_set_error("error creating socket"); - break; - } - - if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) - break; - - /* If we can't connect, try the next one */ - gitno__close(s); - s = INVALID_SOCKET; - } - - /* Oops, we couldn't connect to any address */ - if (s == INVALID_SOCKET && p == NULL) { - giterr_set(GITERR_OS, "Failed to connect to %s", host); - p_freeaddrinfo(info); - return -1; - } - - s_out->socket = s; - p_freeaddrinfo(info); - -#ifdef GIT_SSL - if ((flags & GITNO_CONNECT_SSL) && - (ret = ssl_setup(s_out, host)) < 0) - return ret; -#else - /* SSL is not supported */ - if (flags & GITNO_CONNECT_SSL) { - giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2."); - return -1; - } -#endif - - return 0; -} - -#ifdef GIT_SSL -static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags) -{ - int ret; - size_t off = 0; - - GIT_UNUSED(flags); - - while (off < len) { - ret = SSL_write(ssl->ssl, msg + off, len - off); - if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) - return ssl_set_error(ssl, ret); - - off += ret; - } - - return off; -} -#endif - -int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags) -{ - int ret; - size_t off = 0; - -#ifdef GIT_SSL - if (socket->ssl.ssl) - return gitno_send_ssl(&socket->ssl, msg, len, flags); -#endif - - while (off < len) { - errno = 0; - ret = p_send(socket->socket, msg + off, len - off, flags); - if (ret < 0) { - net_set_error("Error sending data"); - return -1; - } - - off += ret; - } - - return (int)off; -} - -int gitno_close(gitno_socket *s) -{ -#ifdef GIT_SSL - if (s->ssl.ssl && - gitno_ssl_teardown(&s->ssl) < 0) - return -1; -#endif - - return gitno__close(s->socket); -} - -int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) -{ - fd_set fds; - struct timeval tv; - - tv.tv_sec = sec; - tv.tv_usec = usec; - - FD_ZERO(&fds); - FD_SET(buf->socket->socket, &fds); - - /* The select(2) interface is silly */ - return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv); -} - static const char *prefix_http = "http://"; static const char *prefix_https = "https://"; diff --git a/src/netops.h b/src/netops.h index 8ad915301..d5f0ca3f3 100644 --- a/src/netops.h +++ b/src/netops.h @@ -9,6 +9,7 @@ #include "posix.h" #include "common.h" +#include "stream.h" #ifdef GIT_SSL # include <openssl/ssl.h> @@ -32,7 +33,6 @@ typedef struct gitno_buffer { char *data; size_t len; size_t offset; - gitno_socket *socket; int (*recv)(struct gitno_buffer *buffer); void *cb_data; } gitno_buffer; @@ -56,18 +56,13 @@ enum { */ int gitno__match_host(const char *pattern, const char *host); -void gitno_buffer_setup(gitno_socket *t, gitno_buffer *buf, char *data, size_t len); -void gitno_buffer_setup_callback(gitno_socket *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); +void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len); +void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); 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(gitno_socket *socket, const char *host, const char *port, int flags); -int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags); -int gitno_close(gitno_socket *s); -int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); - typedef struct gitno_connection_data { char *host; char *port; diff --git a/src/openssl_stream.c b/src/openssl_stream.c new file mode 100644 index 000000000..3a6369dee --- /dev/null +++ b/src/openssl_stream.c @@ -0,0 +1,375 @@ +/* + * 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. + */ + +#ifdef GIT_SSL + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#include <ctype.h> + +#include "global.h" +#include "posix.h" +#include "stream.h" +#include "socket_stream.h" +#include "netops.h" +#include "git2/transport.h" + +static int ssl_set_error(SSL *ssl, int error) +{ + int err; + unsigned long e; + + err = SSL_get_error(ssl, error); + + assert(err != SSL_ERROR_WANT_READ); + assert(err != SSL_ERROR_WANT_WRITE); + + switch (err) { + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + giterr_set(GITERR_NET, "SSL error: connection failure\n"); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + giterr_set(GITERR_NET, "SSL error: x509 error\n"); + break; + case SSL_ERROR_SYSCALL: + e = ERR_get_error(); + if (e > 0) { + giterr_set(GITERR_NET, "SSL error: %s", + ERR_error_string(e, NULL)); + break; + } else if (error < 0) { + giterr_set(GITERR_OS, "SSL error: syscall failure"); + break; + } + giterr_set(GITERR_NET, "SSL error: received early EOF"); + break; + case SSL_ERROR_SSL: + e = ERR_get_error(); + giterr_set(GITERR_NET, "SSL error: %s", + ERR_error_string(e, NULL)); + break; + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + default: + giterr_set(GITERR_NET, "SSL error: unknown error"); + break; + } + return -1; +} + +static int ssl_teardown(SSL *ssl) +{ + int ret; + + ret = SSL_shutdown(ssl); + if (ret < 0) + ret = ssl_set_error(ssl, ret); + else + ret = 0; + + SSL_free(ssl); + return ret; +} + +static int check_host_name(const char *name, const char *host) +{ + if (!strcasecmp(name, host)) + return 0; + + if (gitno__match_host(name, host) < 0) + return -1; + + return 0; +} + +static int verify_server_cert(SSL *ssl, 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; + + if (SSL_get_verify_result(ssl) != X509_V_OK) { + giterr_set(GITERR_SSL, "The SSL certificate is invalid"); + return GIT_ECERTIFICATE; + } + + /* Try to parse the host as an IP address to see if it is */ + if (p_inet_pton(AF_INET, host, &addr4)) { + type = GEN_IPADD; + addr = &addr4; + } else { + if(p_inet_pton(AF_INET6, host, &addr6)) { + type = GEN_IPADD; + addr = &addr6; + } + } + + + cert = SSL_get_peer_certificate(ssl); + if (!cert) { + giterr_set(GITERR_SSL, "the server did not provide a certificate"); + return -1; + } + + /* 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 cert_fail_name; + + 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_name; + } + + if (check_host_name((char *)peer_cn, host) < 0) + goto cert_fail_name; + + OPENSSL_free(peer_cn); + + return 0; + +on_error: + OPENSSL_free(peer_cn); + return ssl_set_error(ssl, 0); + +cert_fail_name: + OPENSSL_free(peer_cn); + giterr_set(GITERR_SSL, "hostname does not match certificate"); + return GIT_ECERTIFICATE; +} + +typedef struct { + git_stream parent; + git_socket_stream *socket; + SSL *ssl; + git_cert_x509 cert_info; +} openssl_stream; + +int openssl_close(git_stream *stream); + +int openssl_connect(git_stream *stream) +{ + int ret; + openssl_stream *st = (openssl_stream *) stream; + + if ((ret = git_stream_connect((git_stream *)st->socket)) < 0) + return ret; + + if ((ret = SSL_set_fd(st->ssl, st->socket->s)) <= 0) { + openssl_close((git_stream *) st); + return ssl_set_error(st->ssl, ret); + } + + if ((ret = SSL_connect(st->ssl)) <= 0) + return ssl_set_error(st->ssl, ret); + + return verify_server_cert(st->ssl, st->socket->host); +} + +int openssl_certificate(git_cert **out, git_stream *stream) +{ + openssl_stream *st = (openssl_stream *) stream; + int len; + X509 *cert = SSL_get_peer_certificate(st->ssl); + unsigned char *guard, *encoded_cert; + + /* Retrieve the length of the certificate first */ + len = i2d_X509(cert, NULL); + if (len < 0) { + giterr_set(GITERR_NET, "failed to retrieve certificate information"); + return -1; + } + + encoded_cert = git__malloc(len); + GITERR_CHECK_ALLOC(encoded_cert); + /* i2d_X509 makes 'guard' point to just after the data */ + guard = encoded_cert; + + len = i2d_X509(cert, &guard); + if (len < 0) { + git__free(encoded_cert); + giterr_set(GITERR_NET, "failed to retrieve certificate information"); + return -1; + } + + st->cert_info.cert_type = GIT_CERT_X509; + st->cert_info.data = encoded_cert; + st->cert_info.len = len; + + *out = (git_cert *)&st->cert_info; + return 0; +} + +ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + size_t off = 0; + + GIT_UNUSED(flags); + + while (off < len) { + ret = SSL_write(st->ssl, data + off, len - off); + if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) + return ssl_set_error(st->ssl, ret); + + off += ret; + } + + return off; +} + +ssize_t openssl_read(git_stream *stream, void *data, size_t len) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + + do { + ret = SSL_read(st->ssl, data, len); + } while (SSL_get_error(st->ssl, ret) == SSL_ERROR_WANT_READ); + + if (ret < 0) { + ssl_set_error(st->ssl, ret); + return -1; + } + + return ret; +} + +int openssl_close(git_stream *stream) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + + if ((ret = ssl_teardown(st->ssl)) < 0) + return -1; + + return git_stream_close((git_stream *)st->socket); +} + +void openssl_free(git_stream *stream) +{ + openssl_stream *st = (openssl_stream *) stream; + + git__free(st->cert_info.data); + git_stream_free((git_stream *) st->socket); + git__free(st); +} + +int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +{ + openssl_stream *st; + + st = git__calloc(1, sizeof(openssl_stream)); + GITERR_CHECK_ALLOC(st); + + if (git_socket_stream_new((git_stream **) &st->socket, host, port)) + return -1; + + st->ssl = SSL_new(git__ssl_ctx); + if (st->ssl == NULL) { + giterr_set(GITERR_SSL, "failed to create ssl object"); + return -1; + } + + st->parent.version = GIT_STREAM_VERSION; + st->parent.encrypted = 1; + st->parent.connect = openssl_connect; + st->parent.certificate = openssl_certificate; + st->parent.read = openssl_read; + st->parent.write = openssl_write; + st->parent.close = openssl_close; + st->parent.free = openssl_free; + + *out = (git_stream *) st; + return 0; +} + +#else + +#include "stream.h" + +int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +{ + giterr_set(GITERR_SSL, "openssl is not supported in this version"); + return -1; +} + +#endif diff --git a/src/openssl_stream.h b/src/openssl_stream.h new file mode 100644 index 000000000..9ca06489e --- /dev/null +++ b/src/openssl_stream.h @@ -0,0 +1,14 @@ +/* + * 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_openssl_stream_h__ +#define INCLUDE_openssl_stream_h__ + +#include "git2/sys/stream.h" + +extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); + +#endif diff --git a/src/socket_stream.c b/src/socket_stream.c new file mode 100644 index 000000000..71f49118e --- /dev/null +++ b/src/socket_stream.c @@ -0,0 +1,212 @@ +/* + * 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 "common.h" +#include "posix.h" +#include "netops.h" +#include "stream.h" +#include "socket_stream.h" + +#ifndef _WIN32 +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/select.h> +# include <sys/time.h> +# include <netdb.h> +# include <netinet/in.h> +# include <arpa/inet.h> +#else +# include <winsock2.h> +# include <ws2tcpip.h> +# ifdef _MSC_VER +# pragma comment(lib, "ws2_32") +# endif +#endif + +#ifdef GIT_WIN32 +static void net_set_error(const char *str) +{ + int error = WSAGetLastError(); + char * win32_error = git_win32_get_error_message(error); + + if (win32_error) { + giterr_set(GITERR_NET, "%s: %s", str, win32_error); + git__free(win32_error); + } else { + giterr_set(GITERR_NET, str); + } +} +#else +static void net_set_error(const char *str) +{ + giterr_set(GITERR_NET, "%s: %s", str, strerror(errno)); +} +#endif + +static int close_socket(GIT_SOCKET s) +{ + if (s == INVALID_SOCKET) + return 0; + +#ifdef GIT_WIN32 + if (SOCKET_ERROR == closesocket(s)) + return -1; + + if (0 != WSACleanup()) { + giterr_set(GITERR_OS, "Winsock cleanup failed"); + return -1; + } + + return 0; +#else + return close(s); +#endif + +} + +int socket_connect(git_stream *stream) +{ + struct addrinfo *info = NULL, *p; + struct addrinfo hints; + git_socket_stream *st = (git_socket_stream *) stream; + GIT_SOCKET s = INVALID_SOCKET; + int ret; + +#ifdef GIT_WIN32 + /* on win32, the WSA context needs to be initialized + * before any socket calls can be performed */ + WSADATA wsd; + + if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { + giterr_set(GITERR_OS, "Winsock init failed"); + return -1; + } + + if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { + WSACleanup(); + giterr_set(GITERR_OS, "Winsock init failed"); + return -1; + } +#endif + + memset(&hints, 0x0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + + if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) { + giterr_set(GITERR_NET, + "Failed to resolve address for %s: %s", st->host, p_gai_strerror(ret)); + return -1; + } + + for (p = info; p != NULL; p = p->ai_next) { + s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + + if (s == INVALID_SOCKET) { + net_set_error("error creating socket"); + break; + } + + if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) + break; + + /* If we can't connect, try the next one */ + close_socket(s); + s = INVALID_SOCKET; + } + + /* Oops, we couldn't connect to any address */ + if (s == INVALID_SOCKET && p == NULL) { + giterr_set(GITERR_OS, "Failed to connect to %s", st->host); + p_freeaddrinfo(info); + return -1; + } + + st->s = s; + p_freeaddrinfo(info); + return 0; +} + +ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags) +{ + ssize_t ret; + size_t off = 0; + git_socket_stream *st = (git_socket_stream *) stream; + + while (off < len) { + errno = 0; + ret = p_send(st->s, data + off, len - off, flags); + if (ret < 0) { + net_set_error("Error sending data"); + return -1; + } + + off += ret; + } + + return off; +} + +ssize_t socket_read(git_stream *stream, void *data, size_t len) +{ + ssize_t ret; + git_socket_stream *st = (git_socket_stream *) stream; + + if ((ret = p_recv(st->s, data, len, 0)) < 0) + net_set_error("Error receiving socket data"); + + return ret; +} + +int socket_close(git_stream *stream) +{ + git_socket_stream *st = (git_socket_stream *) stream; + int error; + + error = close_socket(st->s); + st->s = INVALID_SOCKET; + + return error; +} + +void socket_free(git_stream *stream) +{ + git_socket_stream *st = (git_socket_stream *) stream; + + git__free(st->host); + git__free(st->port); + git__free(st); +} + +int git_socket_stream_new(git_stream **out, const char *host, const char *port) +{ + git_socket_stream *st; + + assert(out && host); + + st = git__calloc(1, sizeof(git_socket_stream)); + GITERR_CHECK_ALLOC(st); + + st->host = git__strdup(host); + GITERR_CHECK_ALLOC(st->host); + + if (port) { + st->port = git__strdup(port); + GITERR_CHECK_ALLOC(st->port); + } + + st->parent.version = GIT_STREAM_VERSION; + st->parent.connect = socket_connect; + st->parent.write = socket_write; + st->parent.read = socket_read; + st->parent.close = socket_close; + st->parent.free = socket_free; + st->s = INVALID_SOCKET; + + *out = (git_stream *) st; + return 0; +} diff --git a/src/socket_stream.h b/src/socket_stream.h new file mode 100644 index 000000000..8e9949fcd --- /dev/null +++ b/src/socket_stream.h @@ -0,0 +1,21 @@ +/* + * 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_socket_stream_h__ +#define INCLUDE_socket_stream_h__ + +#include "netops.h" + +typedef struct { + git_stream parent; + char *host; + char *port; + GIT_SOCKET s; +} git_socket_stream; + +extern int git_socket_stream_new(git_stream **out, const char *host, const char *port); + +#endif diff --git a/src/stream.h b/src/stream.h new file mode 100644 index 000000000..3a7ef9514 --- /dev/null +++ b/src/stream.h @@ -0,0 +1,48 @@ +/* + * 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_stream_h__ +#define INCLUDE_stream_h__ + +#include "common.h" +#include "git2/sys/stream.h" + +GIT_INLINE(int) git_stream_connect(git_stream *st) +{ + return st->connect(st); +} + +GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st) +{ + if (!st->encrypted) { + giterr_set(GITERR_INVALID, "an unencrypted stream does not have a certificate"); + return -1; + } + + return st->certificate(out, st); +} + +GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len) +{ + return st->read(st, data, len); +} + +GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags) +{ + return st->write(st, data, len, flags); +} + +GIT_INLINE(int) git_stream_close(git_stream *st) +{ + return st->close(st); +} + +GIT_INLINE(void) git_stream_free(git_stream *st) +{ + st->free(st); +} + +#endif diff --git a/src/transports/git.c b/src/transports/git.c index e2690fe36..6f25736b1 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -9,6 +9,8 @@ #include "buffer.h" #include "netops.h" #include "git2/sys/transport.h" +#include "stream.h" +#include "socket_stream.h" #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) @@ -18,16 +20,16 @@ static const char cmd_receivepack[] = "git-receive-pack"; typedef struct { git_smart_subtransport_stream parent; - gitno_socket socket; + git_stream *io; const char *cmd; char *url; unsigned sent_command : 1; -} git_stream; +} git_proto_stream; typedef struct { git_smart_subtransport parent; git_transport *owner; - git_stream *current_stream; + git_proto_stream *current_stream; } git_subtransport; /* @@ -67,7 +69,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) return 0; } -static int send_command(git_stream *s) +static int send_command(git_proto_stream *s) { int error; git_buf request = GIT_BUF_INIT; @@ -76,10 +78,7 @@ static int send_command(git_stream *s) if (error < 0) goto cleanup; - /* It looks like negative values are errors here, and positive values - * are the number of bytes sent. */ - error = gitno_send(&s->socket, request.ptr, request.size, 0); - + error = git_stream_write(s->io, request.ptr, request.size, 0); if (error >= 0) s->sent_command = 1; @@ -88,14 +87,14 @@ cleanup: return error; } -static int git_stream_read( +static int git_proto_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { int error; - git_stream *s = (git_stream *)stream; + git_proto_stream *s = (git_proto_stream *)stream; gitno_buffer buf; *bytes_read = 0; @@ -103,7 +102,7 @@ static int git_stream_read( if (!s->sent_command && (error = send_command(s)) < 0) return error; - gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); + gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size); if ((error = gitno_recv(&buf)) < 0) return error; @@ -113,23 +112,23 @@ static int git_stream_read( return 0; } -static int git_stream_write( +static int git_proto_stream_write( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { int error; - git_stream *s = (git_stream *)stream; + git_proto_stream *s = (git_proto_stream *)stream; if (!s->sent_command && (error = send_command(s)) < 0) return error; - return gitno_send(&s->socket, buffer, len, 0); + return git_stream_write(s->io, buffer, len, 0); } -static void git_stream_free(git_smart_subtransport_stream *stream) +static void git_proto_stream_free(git_smart_subtransport_stream *stream) { - git_stream *s = (git_stream *)stream; + git_proto_stream *s = (git_proto_stream *)stream; git_subtransport *t = OWNING_SUBTRANSPORT(s); int ret; @@ -137,33 +136,31 @@ static void git_stream_free(git_smart_subtransport_stream *stream) t->current_stream = NULL; - if (s->socket.socket) { - ret = gitno_close(&s->socket); - assert(!ret); - } - + git_stream_free(s->io); git__free(s->url); git__free(s); } -static int git_stream_alloc( +static int git_proto_stream_alloc( git_subtransport *t, const char *url, const char *cmd, + const char *host, + const char *port, git_smart_subtransport_stream **stream) { - git_stream *s; + git_proto_stream *s; if (!stream) return -1; - s = git__calloc(sizeof(git_stream), 1); + s = git__calloc(sizeof(git_proto_stream), 1); GITERR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; - s->parent.read = git_stream_read; - s->parent.write = git_stream_write; - s->parent.free = git_stream_free; + s->parent.read = git_proto_stream_read; + s->parent.write = git_proto_stream_write; + s->parent.free = git_proto_stream_free; s->cmd = cmd; s->url = git__strdup(url); @@ -173,6 +170,11 @@ static int git_stream_alloc( return -1; } + if ((git_socket_stream_new(&s->io, host, port)) < 0) + return -1; + + GITERR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream"); + *stream = &s->parent; return 0; } @@ -184,7 +186,7 @@ static int _git_uploadpack_ls( { char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; - git_stream *s; + git_proto_stream *s; int error; *stream = NULL; @@ -192,26 +194,32 @@ static int _git_uploadpack_ls( if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if ((error = git_stream_alloc(t, stream_url, cmd_uploadpack, stream)) < 0) + if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) return error; - s = (git_stream *)*stream; + error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream); - if (!(error = gitno_extract_url_parts( - &host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); - if (!(error = gitno_connect(&s->socket, host, port, 0))) - t->current_stream = s; - git__free(host); - git__free(port); - git__free(path); - git__free(user); - git__free(pass); - } else if (*stream) - git_stream_free(*stream); + if (error < 0) { + git_proto_stream_free(*stream); + return error; + } - return error; + s = (git_proto_stream *) *stream; + if ((error = git_stream_connect(s->io)) < 0) { + git_proto_stream_free(*stream); + return error; + } + + t->current_stream = s; + + return 0; } static int _git_uploadpack( @@ -237,31 +245,37 @@ static int _git_receivepack_ls( { char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; - git_stream *s; + git_proto_stream *s; int error; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0) - return -1; + if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) + return error; + + error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream); + + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); - s = (git_stream *)*stream; + if (error < 0) { + git_proto_stream_free(*stream); + return error; + } + + s = (git_proto_stream *) *stream; - if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { - if (!(error = gitno_connect(&s->socket, host, port, 0))) - t->current_stream = s; + if ((error = git_stream_connect(s->io)) < 0) + return error; - git__free(host); - git__free(port); - git__free(path); - git__free(user); - git__free(pass); - } else if (*stream) - git_stream_free(*stream); + t->current_stream = s; - return error; + return 0; } static int _git_receivepack( diff --git a/src/transports/http.c b/src/transports/http.c index 234ee229f..807e08044 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -13,16 +13,14 @@ #include "smart.h" #include "auth.h" #include "auth_negotiate.h" +#include "openssl_stream.h" +#include "socket_stream.h" git_http_auth_scheme auth_schemes[] = { { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate }, { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic }, }; -#ifdef GIT_SSL -# include <openssl/x509v3.h> -#endif - static const char *upload_pack_service = "upload-pack"; static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack"; static const char *upload_pack_service_url = "/git-upload-pack"; @@ -62,7 +60,7 @@ typedef struct { typedef struct { git_smart_subtransport parent; transport_smart *owner; - gitno_socket socket; + git_stream *io; gitno_connection_data connection_data; bool connected; @@ -474,7 +472,7 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) static void clear_parser_state(http_subtransport *t) { http_parser_init(&t->parser, HTTP_RESPONSE); - gitno_buffer_setup(&t->socket, + gitno_buffer_setup_fromstream(t->io, &t->parse_buffer, t->parse_buffer_data, sizeof(t->parse_buffer_data)); @@ -498,7 +496,7 @@ static void clear_parser_state(http_subtransport *t) git_vector_free_deep(&t->www_authenticate); } -static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) +static int write_chunk(git_stream *io, const char *buffer, size_t len) { git_buf buf = GIT_BUF_INIT; @@ -508,7 +506,7 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) if (git_buf_oom(&buf)) return -1; - if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) { + if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) { git_buf_free(&buf); return -1; } @@ -516,11 +514,11 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) git_buf_free(&buf); /* Chunk body */ - if (len > 0 && gitno_send(socket, buffer, len, 0) < 0) + if (len > 0 && git_stream_write(io, buffer, len, 0) < 0) return -1; /* Chunk footer */ - if (gitno_send(socket, "\r\n", 2, 0) < 0) + if (git_stream_write(io, "\r\n", 2, 0) < 0) return -1; return 0; @@ -528,64 +526,43 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) static int http_connect(http_subtransport *t) { - int flags = 0, error; + int error; if (t->connected && http_should_keep_alive(&t->parser) && t->parse_finished) return 0; - if (t->socket.socket) - gitno_close(&t->socket); + if (t->io) { + git_stream_close(t->io); + git_stream_free(t->io); + t->io = NULL; + } if (t->connection_data.use_ssl) { - int tflags; + error = git_openssl_stream_new(&t->io, t->connection_data.host, t->connection_data.port); + } else { + error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port); + } - if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0) - return -1; + if (error < 0) + return error; - flags |= GITNO_CONNECT_SSL; - } + GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream"); - error = gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags); + error = git_stream_connect(t->io); #ifdef GIT_SSL if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL) { - X509 *cert = SSL_get_peer_certificate(t->socket.ssl.ssl); - git_cert_x509 cert_info, *cert_info_ptr; - int len, is_valid; - unsigned char *guard, *encoded_cert; - - /* Retrieve the length of the certificate first */ - len = i2d_X509(cert, NULL); - if (len < 0) { - giterr_set(GITERR_NET, "failed to retrieve certificate information"); - return -1; - } - + git_cert *cert; + int is_valid; - encoded_cert = git__malloc(len); - GITERR_CHECK_ALLOC(encoded_cert); - /* i2d_X509 makes 'copy' point to just after the data */ - guard = encoded_cert; - - len = i2d_X509(cert, &guard); - if (len < 0) { - git__free(encoded_cert); - giterr_set(GITERR_NET, "failed to retrieve certificate information"); - return -1; - } + if ((error = git_stream_certificate(&cert, t->io)) < 0) + return error; giterr_clear(); is_valid = error != GIT_ECERTIFICATE; - cert_info.cert_type = GIT_CERT_X509; - cert_info.data = encoded_cert; - cert_info.len = len; - - cert_info_ptr = &cert_info; - - error = t->owner->certificate_check_cb((git_cert *) cert_info_ptr, is_valid, t->connection_data.host, t->owner->message_cb_payload); - git__free(encoded_cert); + error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload); if (error < 0) { if (!giterr_last()) @@ -626,7 +603,7 @@ replay: if (gen_request(&request, s, 0) < 0) return -1; - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) { git_buf_free(&request); return -1; } @@ -642,13 +619,13 @@ replay: /* Flush, if necessary */ if (s->chunk_buffer_len > 0 && - write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; /* Write the final chunk. */ - if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0) + if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0) return -1; } @@ -743,7 +720,7 @@ static int http_stream_write_chunked( if (gen_request(&request, s, 0) < 0) return -1; - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) { git_buf_free(&request); return -1; } @@ -756,14 +733,14 @@ static int http_stream_write_chunked( if (len > CHUNK_SIZE) { /* Flush, if necessary */ if (s->chunk_buffer_len > 0) { - if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; } /* Write chunk directly */ - if (write_chunk(&t->socket, buffer, len) < 0) + if (write_chunk(t->io, buffer, len) < 0) return -1; } else { @@ -780,7 +757,7 @@ static int http_stream_write_chunked( /* Is the buffer full? If so, then flush */ if (CHUNK_SIZE == s->chunk_buffer_len) { - if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; @@ -816,10 +793,10 @@ static int http_stream_write_single( if (gen_request(&request, s, len) < 0) return -1; - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) goto on_error; - if (len && gitno_send(&t->socket, buffer, len, 0) < 0) + if (len && git_stream_write(t->io, buffer, len, 0) < 0) goto on_error; git_buf_free(&request); @@ -986,9 +963,10 @@ static int http_close(git_smart_subtransport *subtransport) clear_parser_state(t); - if (t->socket.socket) { - gitno_close(&t->socket); - memset(&t->socket, 0x0, sizeof(gitno_socket)); + if (t->io) { + git_stream_close(t->io); + git_stream_free(t->io); + t->io = NULL; } if (t->cred) { diff --git a/src/transports/smart.c b/src/transports/smart.c index d0f9c90e8..ec0ba3784 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -158,7 +158,7 @@ static int git_smart__connect( /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = stream; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); /* 2 flushes for RPC; 1 for stateful */ if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) @@ -252,7 +252,7 @@ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len if ((error = stream->write(stream, (const char *)data, len)) < 0) return error; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } @@ -278,7 +278,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = *stream; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 1f6716f5d..33d0898ec 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -14,6 +14,7 @@ #include "netops.h" #include "smart.h" #include "cred.h" +#include "socket_stream.h" #ifdef GIT_SSH @@ -25,7 +26,7 @@ static const char cmd_receivepack[] = "git-receive-pack"; typedef struct { git_smart_subtransport_stream parent; - gitno_socket socket; + git_stream *io; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel; const char *cmd; @@ -183,9 +184,10 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream) s->session = NULL; } - if (s->socket.socket) { - (void)gitno_close(&s->socket); - /* can't do anything here with error return value */ + if (s->io) { + git_stream_close(s->io); + git_stream_free(s->io); + s->io = NULL; } git__free(s->url); @@ -413,10 +415,11 @@ static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, static int _git_ssh_session_create( LIBSSH2_SESSION** session, - gitno_socket socket) + git_stream *io) { int rc = 0; LIBSSH2_SESSION* s; + git_socket_stream *socket = (git_socket_stream *) io; assert(session); @@ -427,7 +430,7 @@ static int _git_ssh_session_create( } do { - rc = libssh2_session_startup(s, socket.socket); + rc = libssh2_session_startup(s, socket->s); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { @@ -477,10 +480,11 @@ static int _git_ssh_setup_conn( GITERR_CHECK_ALLOC(port); } - if ((error = gitno_connect(&s->socket, host, port, 0)) < 0) + if ((error = git_socket_stream_new(&s->io, host, port)) < 0 || + (error = git_stream_connect(s->io)) < 0) goto done; - if ((error = _git_ssh_session_create(&session, s->socket)) < 0) + if ((error = _git_ssh_session_create(&session, s->io)) < 0) goto done; if (t->owner->certificate_check_cb != NULL) { |