Blame src/socket_stream.c

Packit ae9e2a
/*
Packit ae9e2a
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit ae9e2a
 *
Packit ae9e2a
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit ae9e2a
 * a Linking Exception. For full terms see the included COPYING file.
Packit ae9e2a
 */
Packit ae9e2a
Packit ae9e2a
#include "common.h"
Packit ae9e2a
#include "posix.h"
Packit ae9e2a
#include "netops.h"
Packit ae9e2a
#include "stream.h"
Packit ae9e2a
#include "socket_stream.h"
Packit ae9e2a
Packit ae9e2a
#ifndef _WIN32
Packit ae9e2a
#	include <sys/types.h>
Packit ae9e2a
#	include <sys/socket.h>
Packit ae9e2a
#	include <sys/select.h>
Packit ae9e2a
#	include <sys/time.h>
Packit ae9e2a
#	include <netdb.h>
Packit ae9e2a
#	include <netinet/in.h>
Packit ae9e2a
#       include <arpa/inet.h>
Packit ae9e2a
#else
Packit ae9e2a
#	include <winsock2.h>
Packit ae9e2a
#	include <ws2tcpip.h>
Packit ae9e2a
#	ifdef _MSC_VER
Packit ae9e2a
#		pragma comment(lib, "ws2_32")
Packit ae9e2a
#	endif
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
#ifdef GIT_WIN32
Packit ae9e2a
static void net_set_error(const char *str)
Packit ae9e2a
{
Packit ae9e2a
	int error = WSAGetLastError();
Packit ae9e2a
	char * win32_error = git_win32_get_error_message(error);
Packit ae9e2a
Packit ae9e2a
	if (win32_error) {
Packit ae9e2a
		giterr_set(GITERR_NET, "%s: %s", str, win32_error);
Packit ae9e2a
		git__free(win32_error);
Packit ae9e2a
	} else {
Packit ae9e2a
		giterr_set(GITERR_NET, str);
Packit ae9e2a
	}
Packit ae9e2a
}
Packit ae9e2a
#else
Packit ae9e2a
static void net_set_error(const char *str)
Packit ae9e2a
{
Packit ae9e2a
	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
Packit ae9e2a
}
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
static int close_socket(GIT_SOCKET s)
Packit ae9e2a
{
Packit ae9e2a
	if (s == INVALID_SOCKET)
Packit ae9e2a
		return 0;
Packit ae9e2a
Packit ae9e2a
#ifdef GIT_WIN32
Packit ae9e2a
	if (SOCKET_ERROR == closesocket(s))
Packit ae9e2a
		return -1;
Packit ae9e2a
Packit ae9e2a
	if (0 != WSACleanup()) {
Packit ae9e2a
		giterr_set(GITERR_OS, "winsock cleanup failed");
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
#else
Packit ae9e2a
	return close(s);
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int socket_connect(git_stream *stream)
Packit ae9e2a
{
Packit ae9e2a
	struct addrinfo *info = NULL, *p;
Packit ae9e2a
	struct addrinfo hints;
Packit ae9e2a
	git_socket_stream *st = (git_socket_stream *) stream;
Packit ae9e2a
	GIT_SOCKET s = INVALID_SOCKET;
Packit ae9e2a
	int ret;
Packit ae9e2a
Packit ae9e2a
#ifdef GIT_WIN32
Packit ae9e2a
	/* on win32, the WSA context needs to be initialized
Packit ae9e2a
	 * before any socket calls can be performed */
Packit ae9e2a
	WSADATA wsd;
Packit ae9e2a
Packit ae9e2a
	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
Packit ae9e2a
		giterr_set(GITERR_OS, "winsock init failed");
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
Packit ae9e2a
		WSACleanup();
Packit ae9e2a
		giterr_set(GITERR_OS, "winsock init failed");
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
	memset(&hints, 0x0, sizeof(struct addrinfo));
Packit ae9e2a
	hints.ai_socktype = SOCK_STREAM;
Packit ae9e2a
	hints.ai_family = AF_UNSPEC;
Packit ae9e2a
Packit ae9e2a
	if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
Packit ae9e2a
		giterr_set(GITERR_NET,
Packit ae9e2a
			   "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	for (p = info; p != NULL; p = p->ai_next) {
Packit ae9e2a
		s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
Packit ae9e2a
Packit ae9e2a
		if (s == INVALID_SOCKET)
Packit ae9e2a
			continue;
Packit ae9e2a
Packit ae9e2a
		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
Packit ae9e2a
			break;
Packit ae9e2a
Packit ae9e2a
		/* If we can't connect, try the next one */
Packit ae9e2a
		close_socket(s);
Packit ae9e2a
		s = INVALID_SOCKET;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	/* Oops, we couldn't connect to any address */
Packit ae9e2a
	if (s == INVALID_SOCKET && p == NULL) {
Packit ae9e2a
		giterr_set(GITERR_OS, "failed to connect to %s", st->host);
Packit ae9e2a
		p_freeaddrinfo(info);
Packit ae9e2a
		return -1;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	st->s = s;
Packit ae9e2a
	p_freeaddrinfo(info);
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
Packit ae9e2a
{
Packit ae9e2a
	ssize_t ret;
Packit ae9e2a
	size_t off = 0;
Packit ae9e2a
	git_socket_stream *st = (git_socket_stream *) stream;
Packit ae9e2a
Packit ae9e2a
	while (off < len) {
Packit ae9e2a
		errno = 0;
Packit ae9e2a
		ret = p_send(st->s, data + off, len - off, flags);
Packit ae9e2a
		if (ret < 0) {
Packit ae9e2a
			net_set_error("Error sending data");
Packit ae9e2a
			return -1;
Packit ae9e2a
		}
Packit ae9e2a
Packit ae9e2a
		off += ret;
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	return off;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
ssize_t socket_read(git_stream *stream, void *data, size_t len)
Packit ae9e2a
{
Packit ae9e2a
	ssize_t ret;
Packit ae9e2a
	git_socket_stream *st = (git_socket_stream *) stream;
Packit ae9e2a
Packit ae9e2a
	if ((ret = p_recv(st->s, data, len, 0)) < 0)
Packit ae9e2a
		net_set_error("Error receiving socket data");
Packit ae9e2a
Packit ae9e2a
	return ret;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int socket_close(git_stream *stream)
Packit ae9e2a
{
Packit ae9e2a
	git_socket_stream *st = (git_socket_stream *) stream;
Packit ae9e2a
	int error;
Packit ae9e2a
Packit ae9e2a
	error = close_socket(st->s);
Packit ae9e2a
	st->s = INVALID_SOCKET;
Packit ae9e2a
Packit ae9e2a
	return error;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
void socket_free(git_stream *stream)
Packit ae9e2a
{
Packit ae9e2a
	git_socket_stream *st = (git_socket_stream *) stream;
Packit ae9e2a
Packit ae9e2a
	git__free(st->host);
Packit ae9e2a
	git__free(st->port);
Packit ae9e2a
	git__free(st);
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_socket_stream_new(git_stream **out, const char *host, const char *port)
Packit ae9e2a
{
Packit ae9e2a
	git_socket_stream *st;
Packit ae9e2a
Packit ae9e2a
	assert(out && host);
Packit ae9e2a
Packit ae9e2a
	st = git__calloc(1, sizeof(git_socket_stream));
Packit ae9e2a
	GITERR_CHECK_ALLOC(st);
Packit ae9e2a
Packit ae9e2a
	st->host = git__strdup(host);
Packit ae9e2a
	GITERR_CHECK_ALLOC(st->host);
Packit ae9e2a
Packit ae9e2a
	if (port) {
Packit ae9e2a
		st->port = git__strdup(port);
Packit ae9e2a
		GITERR_CHECK_ALLOC(st->port);
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	st->parent.version = GIT_STREAM_VERSION;
Packit ae9e2a
	st->parent.connect = socket_connect;
Packit ae9e2a
	st->parent.write = socket_write;
Packit ae9e2a
	st->parent.read = socket_read;
Packit ae9e2a
	st->parent.close = socket_close;
Packit ae9e2a
	st->parent.free = socket_free;
Packit ae9e2a
	st->s = INVALID_SOCKET;
Packit ae9e2a
Packit ae9e2a
	*out = (git_stream *) st;
Packit ae9e2a
	return 0;
Packit ae9e2a
}