summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2014-12-10 11:30:28 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2014-12-10 11:30:28 -0500
commitcd305c2f567678c96bccecab69f715b6063089bc (patch)
tree88ddd29dfe22c8242ef6daeabdf6588cc7ea9d66
parent3b6a566867b3a62aeeed8320f17f1d125e1fe0d2 (diff)
parenta2fd56ab18cfe1a16372193e8ddfbbe6cadb5a78 (diff)
downloadlibgit2-cd305c2f567678c96bccecab69f715b6063089bc.tar.gz
Merge pull request #2678 from libgit2/cmn/io-stream
Introduce stackable IO streams
-rw-r--r--include/git2/sys/stream.h40
-rw-r--r--src/netops.c492
-rw-r--r--src/netops.h11
-rw-r--r--src/openssl_stream.c375
-rw-r--r--src/openssl_stream.h14
-rw-r--r--src/socket_stream.c212
-rw-r--r--src/socket_stream.h21
-rw-r--r--src/stream.h48
-rw-r--r--src/transports/git.c128
-rw-r--r--src/transports/http.c102
-rw-r--r--src/transports/smart.c6
-rw-r--r--src/transports/ssh.c20
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) {