Commit cd305c2f by Edward Thomson

Merge pull request #2678 from libgit2/cmn/io-stream

Introduce stackable IO streams
parents 3b6a5668 a2fd56ab
/*
* 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
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "posix.h" #include "posix.h"
#include "common.h" #include "common.h"
#include "stream.h"
#ifdef GIT_SSL #ifdef GIT_SSL
# include <openssl/ssl.h> # include <openssl/ssl.h>
...@@ -32,7 +33,6 @@ typedef struct gitno_buffer { ...@@ -32,7 +33,6 @@ typedef struct gitno_buffer {
char *data; char *data;
size_t len; size_t len;
size_t offset; size_t offset;
gitno_socket *socket;
int (*recv)(struct gitno_buffer *buffer); int (*recv)(struct gitno_buffer *buffer);
void *cb_data; void *cb_data;
} gitno_buffer; } gitno_buffer;
...@@ -56,18 +56,13 @@ enum { ...@@ -56,18 +56,13 @@ enum {
*/ */
int gitno__match_host(const char *pattern, const char *host); 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_fromstream(git_stream *st, 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_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
int gitno_recv(gitno_buffer *buf); int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons); 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 { typedef struct gitno_connection_data {
char *host; char *host;
char *port; char *port;
......
/*
* 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
/*
* 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
/*
* 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;
}
/*
* 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
/*
* 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
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "buffer.h" #include "buffer.h"
#include "netops.h" #include "netops.h"
#include "git2/sys/transport.h" #include "git2/sys/transport.h"
#include "stream.h"
#include "socket_stream.h"
#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
...@@ -18,16 +20,16 @@ static const char cmd_receivepack[] = "git-receive-pack"; ...@@ -18,16 +20,16 @@ static const char cmd_receivepack[] = "git-receive-pack";
typedef struct { typedef struct {
git_smart_subtransport_stream parent; git_smart_subtransport_stream parent;
gitno_socket socket; git_stream *io;
const char *cmd; const char *cmd;
char *url; char *url;
unsigned sent_command : 1; unsigned sent_command : 1;
} git_stream; } git_proto_stream;
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
git_transport *owner; git_transport *owner;
git_stream *current_stream; git_proto_stream *current_stream;
} git_subtransport; } git_subtransport;
/* /*
...@@ -67,7 +69,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) ...@@ -67,7 +69,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
return 0; return 0;
} }
static int send_command(git_stream *s) static int send_command(git_proto_stream *s)
{ {
int error; int error;
git_buf request = GIT_BUF_INIT; git_buf request = GIT_BUF_INIT;
...@@ -76,10 +78,7 @@ static int send_command(git_stream *s) ...@@ -76,10 +78,7 @@ static int send_command(git_stream *s)
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
/* It looks like negative values are errors here, and positive values error = git_stream_write(s->io, request.ptr, request.size, 0);
* are the number of bytes sent. */
error = gitno_send(&s->socket, request.ptr, request.size, 0);
if (error >= 0) if (error >= 0)
s->sent_command = 1; s->sent_command = 1;
...@@ -88,14 +87,14 @@ cleanup: ...@@ -88,14 +87,14 @@ cleanup:
return error; return error;
} }
static int git_stream_read( static int git_proto_stream_read(
git_smart_subtransport_stream *stream, git_smart_subtransport_stream *stream,
char *buffer, char *buffer,
size_t buf_size, size_t buf_size,
size_t *bytes_read) size_t *bytes_read)
{ {
int error; int error;
git_stream *s = (git_stream *)stream; git_proto_stream *s = (git_proto_stream *)stream;
gitno_buffer buf; gitno_buffer buf;
*bytes_read = 0; *bytes_read = 0;
...@@ -103,7 +102,7 @@ static int git_stream_read( ...@@ -103,7 +102,7 @@ static int git_stream_read(
if (!s->sent_command && (error = send_command(s)) < 0) if (!s->sent_command && (error = send_command(s)) < 0)
return error; 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) if ((error = gitno_recv(&buf)) < 0)
return error; return error;
...@@ -113,23 +112,23 @@ static int git_stream_read( ...@@ -113,23 +112,23 @@ static int git_stream_read(
return 0; return 0;
} }
static int git_stream_write( static int git_proto_stream_write(
git_smart_subtransport_stream *stream, git_smart_subtransport_stream *stream,
const char *buffer, const char *buffer,
size_t len) size_t len)
{ {
int error; 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) if (!s->sent_command && (error = send_command(s)) < 0)
return error; 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); git_subtransport *t = OWNING_SUBTRANSPORT(s);
int ret; int ret;
...@@ -137,33 +136,31 @@ static void git_stream_free(git_smart_subtransport_stream *stream) ...@@ -137,33 +136,31 @@ static void git_stream_free(git_smart_subtransport_stream *stream)
t->current_stream = NULL; t->current_stream = NULL;
if (s->socket.socket) { git_stream_free(s->io);
ret = gitno_close(&s->socket);
assert(!ret);
}
git__free(s->url); git__free(s->url);
git__free(s); git__free(s);
} }
static int git_stream_alloc( static int git_proto_stream_alloc(
git_subtransport *t, git_subtransport *t,
const char *url, const char *url,
const char *cmd, const char *cmd,
const char *host,
const char *port,
git_smart_subtransport_stream **stream) git_smart_subtransport_stream **stream)
{ {
git_stream *s; git_proto_stream *s;
if (!stream) if (!stream)
return -1; return -1;
s = git__calloc(sizeof(git_stream), 1); s = git__calloc(sizeof(git_proto_stream), 1);
GITERR_CHECK_ALLOC(s); GITERR_CHECK_ALLOC(s);
s->parent.subtransport = &t->parent; s->parent.subtransport = &t->parent;
s->parent.read = git_stream_read; s->parent.read = git_proto_stream_read;
s->parent.write = git_stream_write; s->parent.write = git_proto_stream_write;
s->parent.free = git_stream_free; s->parent.free = git_proto_stream_free;
s->cmd = cmd; s->cmd = cmd;
s->url = git__strdup(url); s->url = git__strdup(url);
...@@ -173,6 +170,11 @@ static int git_stream_alloc( ...@@ -173,6 +170,11 @@ static int git_stream_alloc(
return -1; 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; *stream = &s->parent;
return 0; return 0;
} }
...@@ -184,7 +186,7 @@ static int _git_uploadpack_ls( ...@@ -184,7 +186,7 @@ static int _git_uploadpack_ls(
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *stream_url = url; const char *stream_url = url;
git_stream *s; git_proto_stream *s;
int error; int error;
*stream = NULL; *stream = NULL;
...@@ -192,26 +194,32 @@ static int _git_uploadpack_ls( ...@@ -192,26 +194,32 @@ static int _git_uploadpack_ls(
if (!git__prefixcmp(url, prefix_git)) if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(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; 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( git__free(host);
&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { 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); if (error < 0) {
git__free(port); git_proto_stream_free(*stream);
git__free(path); return error;
git__free(user); }
git__free(pass);
} else if (*stream)
git_stream_free(*stream);
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( static int _git_uploadpack(
...@@ -237,31 +245,37 @@ static int _git_receivepack_ls( ...@@ -237,31 +245,37 @@ static int _git_receivepack_ls(
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *stream_url = url; const char *stream_url = url;
git_stream *s; git_proto_stream *s;
int error; int error;
*stream = NULL; *stream = NULL;
if (!git__prefixcmp(url, prefix_git)) if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(prefix_git); stream_url += strlen(prefix_git);
if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0) if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
return -1; 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 = git_stream_connect(s->io)) < 0)
if (!(error = gitno_connect(&s->socket, host, port, 0))) return error;
t->current_stream = s;
git__free(host); t->current_stream = s;
git__free(port);
git__free(path);
git__free(user);
git__free(pass);
} else if (*stream)
git_stream_free(*stream);
return error; return 0;
} }
static int _git_receivepack( static int _git_receivepack(
......
...@@ -13,16 +13,14 @@ ...@@ -13,16 +13,14 @@
#include "smart.h" #include "smart.h"
#include "auth.h" #include "auth.h"
#include "auth_negotiate.h" #include "auth_negotiate.h"
#include "openssl_stream.h"
#include "socket_stream.h"
git_http_auth_scheme auth_schemes[] = { git_http_auth_scheme auth_schemes[] = {
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate }, { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
{ GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic }, { 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_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-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"; static const char *upload_pack_service_url = "/git-upload-pack";
...@@ -62,7 +60,7 @@ typedef struct { ...@@ -62,7 +60,7 @@ typedef struct {
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_socket socket; git_stream *io;
gitno_connection_data connection_data; gitno_connection_data connection_data;
bool connected; bool connected;
...@@ -474,7 +472,7 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) ...@@ -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) static void clear_parser_state(http_subtransport *t)
{ {
http_parser_init(&t->parser, HTTP_RESPONSE); http_parser_init(&t->parser, HTTP_RESPONSE);
gitno_buffer_setup(&t->socket, gitno_buffer_setup_fromstream(t->io,
&t->parse_buffer, &t->parse_buffer,
t->parse_buffer_data, t->parse_buffer_data,
sizeof(t->parse_buffer_data)); sizeof(t->parse_buffer_data));
...@@ -498,7 +496,7 @@ static void clear_parser_state(http_subtransport *t) ...@@ -498,7 +496,7 @@ static void clear_parser_state(http_subtransport *t)
git_vector_free_deep(&t->www_authenticate); 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; git_buf buf = GIT_BUF_INIT;
...@@ -508,7 +506,7 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) ...@@ -508,7 +506,7 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
if (git_buf_oom(&buf)) if (git_buf_oom(&buf))
return -1; 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); git_buf_free(&buf);
return -1; return -1;
} }
...@@ -516,11 +514,11 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) ...@@ -516,11 +514,11 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
git_buf_free(&buf); git_buf_free(&buf);
/* Chunk body */ /* 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; return -1;
/* Chunk footer */ /* Chunk footer */
if (gitno_send(socket, "\r\n", 2, 0) < 0) if (git_stream_write(io, "\r\n", 2, 0) < 0)
return -1; return -1;
return 0; return 0;
...@@ -528,64 +526,43 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) ...@@ -528,64 +526,43 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
static int http_connect(http_subtransport *t) static int http_connect(http_subtransport *t)
{ {
int flags = 0, error; int error;
if (t->connected && if (t->connected &&
http_should_keep_alive(&t->parser) && http_should_keep_alive(&t->parser) &&
t->parse_finished) t->parse_finished)
return 0; return 0;
if (t->socket.socket) if (t->io) {
gitno_close(&t->socket); git_stream_close(t->io);
git_stream_free(t->io);
t->io = NULL;
}
if (t->connection_data.use_ssl) { 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) if (error < 0)
return -1; 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 #ifdef GIT_SSL
if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL) { if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL) {
X509 *cert = SSL_get_peer_certificate(t->socket.ssl.ssl); git_cert *cert;
git_cert_x509 cert_info, *cert_info_ptr; int is_valid;
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;
}
encoded_cert = git__malloc(len); if ((error = git_stream_certificate(&cert, t->io)) < 0)
GITERR_CHECK_ALLOC(encoded_cert); return error;
/* 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;
}
giterr_clear(); giterr_clear();
is_valid = error != GIT_ECERTIFICATE; is_valid = error != GIT_ECERTIFICATE;
cert_info.cert_type = GIT_CERT_X509; error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
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);
if (error < 0) { if (error < 0) {
if (!giterr_last()) if (!giterr_last())
...@@ -626,7 +603,7 @@ replay: ...@@ -626,7 +603,7 @@ replay:
if (gen_request(&request, s, 0) < 0) if (gen_request(&request, s, 0) < 0)
return -1; 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); git_buf_free(&request);
return -1; return -1;
} }
...@@ -642,13 +619,13 @@ replay: ...@@ -642,13 +619,13 @@ replay:
/* Flush, if necessary */ /* Flush, if necessary */
if (s->chunk_buffer_len > 0 && 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; return -1;
s->chunk_buffer_len = 0; s->chunk_buffer_len = 0;
/* Write the final chunk. */ /* 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; return -1;
} }
...@@ -743,7 +720,7 @@ static int http_stream_write_chunked( ...@@ -743,7 +720,7 @@ static int http_stream_write_chunked(
if (gen_request(&request, s, 0) < 0) if (gen_request(&request, s, 0) < 0)
return -1; 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); git_buf_free(&request);
return -1; return -1;
} }
...@@ -756,14 +733,14 @@ static int http_stream_write_chunked( ...@@ -756,14 +733,14 @@ static int http_stream_write_chunked(
if (len > CHUNK_SIZE) { if (len > CHUNK_SIZE) {
/* Flush, if necessary */ /* Flush, if necessary */
if (s->chunk_buffer_len > 0) { 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; return -1;
s->chunk_buffer_len = 0; s->chunk_buffer_len = 0;
} }
/* Write chunk directly */ /* Write chunk directly */
if (write_chunk(&t->socket, buffer, len) < 0) if (write_chunk(t->io, buffer, len) < 0)
return -1; return -1;
} }
else { else {
...@@ -780,7 +757,7 @@ static int http_stream_write_chunked( ...@@ -780,7 +757,7 @@ static int http_stream_write_chunked(
/* Is the buffer full? If so, then flush */ /* Is the buffer full? If so, then flush */
if (CHUNK_SIZE == s->chunk_buffer_len) { 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; return -1;
s->chunk_buffer_len = 0; s->chunk_buffer_len = 0;
...@@ -816,10 +793,10 @@ static int http_stream_write_single( ...@@ -816,10 +793,10 @@ static int http_stream_write_single(
if (gen_request(&request, s, len) < 0) if (gen_request(&request, s, len) < 0)
return -1; 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; 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; goto on_error;
git_buf_free(&request); git_buf_free(&request);
...@@ -986,9 +963,10 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -986,9 +963,10 @@ static int http_close(git_smart_subtransport *subtransport)
clear_parser_state(t); clear_parser_state(t);
if (t->socket.socket) { if (t->io) {
gitno_close(&t->socket); git_stream_close(t->io);
memset(&t->socket, 0x0, sizeof(gitno_socket)); git_stream_free(t->io);
t->io = NULL;
} }
if (t->cred) { if (t->cred) {
......
...@@ -158,7 +158,7 @@ static int git_smart__connect( ...@@ -158,7 +158,7 @@ static int git_smart__connect(
/* Save off the current stream (i.e. socket) that we are working with */ /* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream; 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 */ /* 2 flushes for RPC; 1 for stateful */
if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) 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 ...@@ -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) if ((error = stream->write(stream, (const char *)data, len)) < 0)
return error; 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; return 0;
} }
...@@ -278,7 +278,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream ...@@ -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 */ /* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = *stream; 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; return 0;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "netops.h" #include "netops.h"
#include "smart.h" #include "smart.h"
#include "cred.h" #include "cred.h"
#include "socket_stream.h"
#ifdef GIT_SSH #ifdef GIT_SSH
...@@ -25,7 +26,7 @@ static const char cmd_receivepack[] = "git-receive-pack"; ...@@ -25,7 +26,7 @@ static const char cmd_receivepack[] = "git-receive-pack";
typedef struct { typedef struct {
git_smart_subtransport_stream parent; git_smart_subtransport_stream parent;
gitno_socket socket; git_stream *io;
LIBSSH2_SESSION *session; LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel; LIBSSH2_CHANNEL *channel;
const char *cmd; const char *cmd;
...@@ -183,9 +184,10 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream) ...@@ -183,9 +184,10 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream)
s->session = NULL; s->session = NULL;
} }
if (s->socket.socket) { if (s->io) {
(void)gitno_close(&s->socket); git_stream_close(s->io);
/* can't do anything here with error return value */ git_stream_free(s->io);
s->io = NULL;
} }
git__free(s->url); git__free(s->url);
...@@ -413,10 +415,11 @@ static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, ...@@ -413,10 +415,11 @@ static int request_creds(git_cred **out, ssh_subtransport *t, const char *user,
static int _git_ssh_session_create( static int _git_ssh_session_create(
LIBSSH2_SESSION** session, LIBSSH2_SESSION** session,
gitno_socket socket) git_stream *io)
{ {
int rc = 0; int rc = 0;
LIBSSH2_SESSION* s; LIBSSH2_SESSION* s;
git_socket_stream *socket = (git_socket_stream *) io;
assert(session); assert(session);
...@@ -427,7 +430,7 @@ static int _git_ssh_session_create( ...@@ -427,7 +430,7 @@ static int _git_ssh_session_create(
} }
do { do {
rc = libssh2_session_startup(s, socket.socket); rc = libssh2_session_startup(s, socket->s);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (rc != LIBSSH2_ERROR_NONE) { if (rc != LIBSSH2_ERROR_NONE) {
...@@ -477,10 +480,11 @@ static int _git_ssh_setup_conn( ...@@ -477,10 +480,11 @@ static int _git_ssh_setup_conn(
GITERR_CHECK_ALLOC(port); 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; goto done;
if ((error = _git_ssh_session_create(&session, s->socket)) < 0) if ((error = _git_ssh_session_create(&session, s->io)) < 0)
goto done; goto done;
if (t->owner->certificate_check_cb != NULL) { if (t->owner->certificate_check_cb != NULL) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment