]> git.proxmox.com Git - libgit2.git/blame - src/libgit2/streams/socket.c
New upstream version 1.5.0+ds
[libgit2.git] / src / libgit2 / streams / socket.c
CommitLineData
dd4ff2c9
CMN
1/*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
eae0bfdc
PP
8#include "streams/socket.h"
9
dd4ff2c9 10#include "posix.h"
dd4ff2c9 11#include "netops.h"
ac3d33df 12#include "registry.h"
468d7b11 13#include "stream.h"
dd4ff2c9
CMN
14
15#ifndef _WIN32
16# include <sys/types.h>
17# include <sys/socket.h>
18# include <sys/select.h>
19# include <sys/time.h>
20# include <netdb.h>
21# include <netinet/in.h>
22# include <arpa/inet.h>
23#else
24# include <winsock2.h>
25# include <ws2tcpip.h>
26# ifdef _MSC_VER
27# pragma comment(lib, "ws2_32")
28# endif
29#endif
30
31#ifdef GIT_WIN32
32static void net_set_error(const char *str)
33{
34 int error = WSAGetLastError();
35 char * win32_error = git_win32_get_error_message(error);
36
37 if (win32_error) {
ac3d33df 38 git_error_set(GIT_ERROR_NET, "%s: %s", str, win32_error);
dd4ff2c9
CMN
39 git__free(win32_error);
40 } else {
ac3d33df 41 git_error_set(GIT_ERROR_NET, "%s", str);
dd4ff2c9
CMN
42 }
43}
44#else
45static void net_set_error(const char *str)
46{
ac3d33df 47 git_error_set(GIT_ERROR_NET, "%s: %s", str, strerror(errno));
dd4ff2c9
CMN
48}
49#endif
50
51static int close_socket(GIT_SOCKET s)
52{
53 if (s == INVALID_SOCKET)
54 return 0;
55
56#ifdef GIT_WIN32
57 if (SOCKET_ERROR == closesocket(s))
58 return -1;
59
60 if (0 != WSACleanup()) {
ac3d33df 61 git_error_set(GIT_ERROR_OS, "winsock cleanup failed");
dd4ff2c9
CMN
62 return -1;
63 }
64
65 return 0;
66#else
67 return close(s);
68#endif
69
70}
71
ac3d33df 72static int socket_connect(git_stream *stream)
dd4ff2c9
CMN
73{
74 struct addrinfo *info = NULL, *p;
75 struct addrinfo hints;
468d7b11 76 git_socket_stream *st = (git_socket_stream *) stream;
dd4ff2c9
CMN
77 GIT_SOCKET s = INVALID_SOCKET;
78 int ret;
79
80#ifdef GIT_WIN32
81 /* on win32, the WSA context needs to be initialized
82 * before any socket calls can be performed */
83 WSADATA wsd;
84
85 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
ac3d33df 86 git_error_set(GIT_ERROR_OS, "winsock init failed");
dd4ff2c9
CMN
87 return -1;
88 }
89
90 if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
91 WSACleanup();
ac3d33df 92 git_error_set(GIT_ERROR_OS, "winsock init failed");
dd4ff2c9
CMN
93 return -1;
94 }
95#endif
96
97 memset(&hints, 0x0, sizeof(struct addrinfo));
98 hints.ai_socktype = SOCK_STREAM;
99 hints.ai_family = AF_UNSPEC;
100
101 if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
ac3d33df 102 git_error_set(GIT_ERROR_NET,
909d5494 103 "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
dd4ff2c9
CMN
104 return -1;
105 }
106
107 for (p = info; p != NULL; p = p->ai_next) {
eae0bfdc 108 s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
dd4ff2c9 109
954e06a8
PS
110 if (s == INVALID_SOCKET)
111 continue;
dd4ff2c9
CMN
112
113 if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
114 break;
115
116 /* If we can't connect, try the next one */
117 close_socket(s);
118 s = INVALID_SOCKET;
119 }
120
121 /* Oops, we couldn't connect to any address */
122 if (s == INVALID_SOCKET && p == NULL) {
ac3d33df 123 git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
dd4ff2c9
CMN
124 p_freeaddrinfo(info);
125 return -1;
126 }
127
128 st->s = s;
129 p_freeaddrinfo(info);
130 return 0;
131}
132
ac3d33df 133static ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
dd4ff2c9 134{
468d7b11 135 git_socket_stream *st = (git_socket_stream *) stream;
ac3d33df 136 ssize_t written;
dd4ff2c9 137
ac3d33df 138 errno = 0;
dd4ff2c9 139
ac3d33df 140 if ((written = p_send(st->s, data, len, flags)) < 0) {
22a2d3d5 141 net_set_error("error sending data");
ac3d33df 142 return -1;
dd4ff2c9
CMN
143 }
144
ac3d33df 145 return written;
dd4ff2c9
CMN
146}
147
ac3d33df 148static ssize_t socket_read(git_stream *stream, void *data, size_t len)
dd4ff2c9
CMN
149{
150 ssize_t ret;
468d7b11 151 git_socket_stream *st = (git_socket_stream *) stream;
dd4ff2c9
CMN
152
153 if ((ret = p_recv(st->s, data, len, 0)) < 0)
22a2d3d5 154 net_set_error("error receiving socket data");
dd4ff2c9
CMN
155
156 return ret;
157}
158
ac3d33df 159static int socket_close(git_stream *stream)
dd4ff2c9 160{
468d7b11 161 git_socket_stream *st = (git_socket_stream *) stream;
dd4ff2c9
CMN
162 int error;
163
164 error = close_socket(st->s);
165 st->s = INVALID_SOCKET;
166
167 return error;
168}
169
ac3d33df 170static void socket_free(git_stream *stream)
dd4ff2c9 171{
468d7b11 172 git_socket_stream *st = (git_socket_stream *) stream;
dd4ff2c9
CMN
173
174 git__free(st->host);
175 git__free(st->port);
176 git__free(st);
177}
178
ac3d33df
JK
179static int default_socket_stream_new(
180 git_stream **out,
181 const char *host,
182 const char *port)
dd4ff2c9 183{
468d7b11 184 git_socket_stream *st;
dd4ff2c9 185
c25aa7cd
PP
186 GIT_ASSERT_ARG(out);
187 GIT_ASSERT_ARG(host);
188 GIT_ASSERT_ARG(port);
dd4ff2c9 189
468d7b11 190 st = git__calloc(1, sizeof(git_socket_stream));
ac3d33df 191 GIT_ERROR_CHECK_ALLOC(st);
dd4ff2c9
CMN
192
193 st->host = git__strdup(host);
ac3d33df 194 GIT_ERROR_CHECK_ALLOC(st->host);
dd4ff2c9
CMN
195
196 if (port) {
197 st->port = git__strdup(port);
ac3d33df 198 GIT_ERROR_CHECK_ALLOC(st->port);
dd4ff2c9
CMN
199 }
200
201 st->parent.version = GIT_STREAM_VERSION;
202 st->parent.connect = socket_connect;
203 st->parent.write = socket_write;
204 st->parent.read = socket_read;
205 st->parent.close = socket_close;
206 st->parent.free = socket_free;
207 st->s = INVALID_SOCKET;
208
209 *out = (git_stream *) st;
210 return 0;
211}
ac3d33df
JK
212
213int git_socket_stream_new(
214 git_stream **out,
215 const char *host,
216 const char *port)
217{
218 int (*init)(git_stream **, const char *, const char *) = NULL;
219 git_stream_registration custom = {0};
220 int error;
221
c25aa7cd
PP
222 GIT_ASSERT_ARG(out);
223 GIT_ASSERT_ARG(host);
224 GIT_ASSERT_ARG(port);
ac3d33df
JK
225
226 if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0)
227 init = custom.init;
228 else if (error == GIT_ENOTFOUND)
229 init = default_socket_stream_new;
230 else
231 return error;
232
233 if (!init) {
234 git_error_set(GIT_ERROR_NET, "there is no socket stream available");
235 return -1;
236 }
237
238 return init(out, host, port);
239}