]> git.proxmox.com Git - libgit2.git/blame - src/netops.c
https: make it work with OpenSSL as well
[libgit2.git] / src / netops.c
CommitLineData
1b4f8140 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
1b4f8140 3 *
bb742ede
VM
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.
1b4f8140 6 */
39cdf272 7#ifndef _WIN32
6e34111e
VM
8# include <sys/types.h>
9# include <sys/socket.h>
10# include <sys/select.h>
31ffc141 11# include <sys/time.h>
6e34111e 12# include <netdb.h>
4e95ef02 13#else
6e34111e
VM
14# include <winsock2.h>
15# include <Ws2tcpip.h>
16# ifdef _MSC_VER
17# pragma comment(lib, "Ws2_32.lib")
18# endif
4e95ef02 19#endif
1b4f8140 20
66024c7c
CMN
21#ifdef GIT_GNUTLS
22# include <gnutls/openssl.h>
23# include <gnutls/gnutls.h>
24# include <gnutls/x509.h>
a6f24a5b
CMN
25#elif defined(GIT_OPENSSL)
26# include <openssl/ssl.h>
66024c7c 27#endif
6e34111e 28
a6f24a5b 29
1b4f8140
CMN
30#include "git2/errors.h"
31
32#include "common.h"
33#include "netops.h"
34bfb4b0 34#include "posix.h"
bd6585a7 35#include "buffer.h"
66024c7c 36#include "transport.h"
bd6585a7
CMN
37
38#ifdef GIT_WIN32
39static void net_set_error(const char *str)
40{
41 int size, error = WSAGetLastError();
42 LPSTR err_str = NULL;
43
44 size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
45 0, error, 0, (LPSTR)&err_str, 0, 0);
46
65ca81a6 47 giterr_set(GITERR_NET, "%s: %s", str, err_str);
bd6585a7
CMN
48 LocalFree(err_str);
49}
50#else
51static void net_set_error(const char *str)
52{
53 giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
54}
55#endif
1b4f8140 56
66024c7c
CMN
57#ifdef GIT_GNUTLS
58static int ssl_set_error(int error)
59{
60 giterr_set(GITERR_NET, "SSL error: (%s) %s", gnutls_strerror_name(error), gnutls_strerror(error));
61 return -1;
62}
a6f24a5b
CMN
63#elif GIT_OPENSSL
64static int ssl_set_error(gitno_ssl *ssl, int error)
65{
66 int err;
67 err = SSL_get_error(ssl->ssl, error);
68 giterr_set(GITERR_NET, "SSL error: %s", ERR_error_string(err, NULL));
69 return -1;
70}
66024c7c
CMN
71#endif
72
73void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len)
ea7a5452
CMN
74{
75 memset(buf, 0x0, sizeof(gitno_buffer));
76 memset(data, 0x0, len);
77 buf->data = data;
b0bda0a4 78 buf->len = len;
ea7a5452 79 buf->offset = 0;
66024c7c 80 buf->fd = t->socket;
a6f24a5b 81#ifdef GIT_SSL
66024c7c 82 if (t->encrypt)
a6f24a5b 83 buf->ssl = &t->ssl;
66024c7c
CMN
84#endif
85}
86
a6f24a5b 87#ifdef GIT_GNUTLS
66024c7c
CMN
88static int ssl_recv(gitno_ssl *ssl, void *data, size_t len)
89{
90 int ret;
91
92 do {
93 ret = gnutls_record_recv(ssl->session, data, len);
94 } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
95
96 if (ret < 0) {
97 ssl_set_error(ret);
98 return -1;
99 }
100
101 return ret;
ea7a5452 102}
a6f24a5b
CMN
103#elif defined(GIT_OPENSSL)
104static int ssl_recv(gitno_ssl *ssl, void *data, size_t len)
105{
106 int ret;
107
108 do {
109 ret = SSL_read(ssl->ssl, data, len);
110 } while (SSL_get_error(ssl->ssl, ret) == SSL_ERROR_WANT_READ);
111
112 if (ret < 0)
113 return ssl_set_error(ssl, ret);
114
115 return ret;
116}
117#endif
ea7a5452
CMN
118
119int gitno_recv(gitno_buffer *buf)
120{
121 int ret;
122
a6f24a5b 123#ifdef GIT_SSL
66024c7c
CMN
124 if (buf->ssl != NULL) {
125 if ((ret = ssl_recv(buf->ssl, buf->data + buf->offset, buf->len - buf->offset)) < 0)
126 return -1;
127 } else {
128 ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
129 if (ret < 0) {
130 net_set_error("Error receiving socket data");
131 return -1;
132 }
133 }
134#else
44ef8b1b 135 ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
56b7df10 136 if (ret < 0) {
44ef8b1b 137 net_set_error("Error receiving socket data");
56b7df10
CMN
138 return -1;
139 }
66024c7c 140#endif
ea7a5452
CMN
141
142 buf->offset += ret;
ea7a5452
CMN
143 return ret;
144}
145
146/* Consume up to ptr and move the rest of the buffer to the beginning */
c7c787ce 147void gitno_consume(gitno_buffer *buf, const char *ptr)
ea7a5452 148{
0bd594b6 149 size_t consumed;
ea7a5452 150
0bd594b6 151 assert(ptr - buf->data >= 0);
ea7a5452
CMN
152 assert(ptr - buf->data <= (int) buf->len);
153
c7c787ce 154 consumed = ptr - buf->data;
ea7a5452 155
c7c787ce
CMN
156 memmove(buf->data, ptr, buf->offset - consumed);
157 memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
158 buf->offset -= consumed;
ea7a5452
CMN
159}
160
161/* Consume const bytes and move the rest of the buffer to the beginning */
0bd594b6 162void gitno_consume_n(gitno_buffer *buf, size_t cons)
ea7a5452
CMN
163{
164 memmove(buf->data, buf->data + cons, buf->len - buf->offset);
165 memset(buf->data + cons, 0x0, buf->len - buf->offset);
166 buf->offset -= cons;
167}
168
66024c7c
CMN
169#ifdef GIT_GNUTLS
170static int ssl_setup(git_transport *t)
171{
172 int ret;
173
174 if ((ret = gnutls_global_init()) < 0)
175 return ssl_set_error(ret);
176
177 if ((ret = gnutls_certificate_allocate_credentials(&t->ssl.cred)) < 0)
178 return ssl_set_error(ret);
179
180 gnutls_init(&t->ssl.session, GNUTLS_CLIENT);
181 //gnutls_certificate_set_verify_function(ssl->cred, SSL_VERIFY_NONE);
182 gnutls_credentials_set(t->ssl.session, GNUTLS_CRD_CERTIFICATE, t->ssl.cred);
183
184 if ((ret = gnutls_priority_set_direct (t->ssl.session, "NORMAL", NULL)) < 0)
185 return ssl_set_error(ret);
186
187 gnutls_transport_set_ptr(t->ssl.session, (gnutls_transport_ptr_t) t->socket);
188
189 do {
190 ret = gnutls_handshake(t->ssl.session);
191 } while (ret < 0 && !gnutls_error_is_fatal(ret));
192
193 if (ret < 0) {
194 ssl_set_error(ret);
195 goto on_error;
196 }
197
198 return 0;
199
200on_error:
201 gnutls_deinit(t->ssl.session);
202 return -1;
203}
a6f24a5b
CMN
204#elif defined(GIT_OPENSSL)
205static int ssl_setup(git_transport *t)
206{
207 int ret;
208
209 SSL_library_init();
210 SSL_load_error_strings();
211 t->ssl.ctx = SSL_CTX_new(SSLv23_method());
212 if (t->ssl.ctx == NULL)
213 return ssl_set_error(&t->ssl, 0);
214
215 SSL_CTX_set_mode(t->ssl.ctx, SSL_MODE_AUTO_RETRY);
216
217 t->ssl.ssl = SSL_new(t->ssl.ctx);
218 if (t->ssl.ssl == NULL)
219 return ssl_set_error(&t->ssl, 0);
220
221 if((ret = SSL_set_fd(t->ssl.ssl, t->socket)) == 0)
222 return ssl_set_error(&t->ssl, ret);
223
224 if ((ret = SSL_connect(t->ssl.ssl)) <= 0)
225 return ssl_set_error(&t->ssl, ret);
226
227 return 0;
228}
66024c7c
CMN
229#endif
230
231int gitno_connect(git_transport *t, const char *host, const char *port)
1b4f8140 232{
44ef8b1b 233 struct addrinfo *info = NULL, *p;
1b4f8140 234 struct addrinfo hints;
56b7df10 235 int ret;
44ef8b1b 236 GIT_SOCKET s = INVALID_SOCKET;
1b4f8140
CMN
237
238 memset(&hints, 0x0, sizeof(struct addrinfo));
239 hints.ai_family = AF_UNSPEC;
240 hints.ai_socktype = SOCK_STREAM;
241
56b7df10
CMN
242 if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {
243 giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret));
cd58c15c 244 return -1;
1b4f8140
CMN
245 }
246
247 for (p = info; p != NULL; p = p->ai_next) {
248 s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
ccc9872d 249 if (s == INVALID_SOCKET) {
44ef8b1b
RB
250 net_set_error("error creating socket");
251 break;
1b4f8140
CMN
252 }
253
44ef8b1b
RB
254 if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
255 break;
1b4f8140 256
44ef8b1b
RB
257 /* If we can't connect, try the next one */
258 gitno_close(s);
259 s = INVALID_SOCKET;
1b4f8140
CMN
260 }
261
262 /* Oops, we couldn't connect to any address */
cd58c15c 263 if (s == INVALID_SOCKET && p == NULL) {
44ef8b1b 264 giterr_set(GITERR_OS, "Failed to connect to %s", host);
cd58c15c
VM
265 return -1;
266 }
44ef8b1b 267
66024c7c 268 t->socket = s;
44ef8b1b 269 freeaddrinfo(info);
66024c7c 270
a6f24a5b 271#ifdef GIT_SSL
66024c7c
CMN
272 if (t->encrypt && ssl_setup(t) < 0)
273 return -1;
274#endif
275
cd58c15c 276 return 0;
1b4f8140 277}
4e95ef02 278
66024c7c
CMN
279#ifdef GIT_GNUTLS
280static int send_ssl(gitno_ssl *ssl, const char *msg, size_t len)
4e95ef02 281{
0bd594b6
VM
282 int ret;
283 size_t off = 0;
4e95ef02
CMN
284
285 while (off < len) {
66024c7c
CMN
286 ret = gnutls_record_send(ssl->session, msg + off, len - off);
287 if (ret < 0) {
288 if (gnutls_error_is_fatal(ret))
289 return ssl_set_error(ret);
ccc9872d 290
66024c7c
CMN
291 ret = 0;
292 }
293 off += ret;
294 }
295
296 return off;
297}
a6f24a5b
CMN
298#elif defined(GIT_OPENSSL)
299static int send_ssl(gitno_ssl *ssl, const char *msg, size_t len)
300{
301 int ret;
302 size_t off = 0;
303
304 while (off < len) {
305 ret = SSL_write(ssl->ssl, msg + off, len - off);
306 if (ret <= 0)
307 return ssl_set_error(ssl, ret);
308
309 off += ret;
310 }
311
312 return off;
313}
66024c7c
CMN
314#endif
315
316int gitno_send(git_transport *t, const char *msg, size_t len, int flags)
317{
318 int ret;
319 size_t off = 0;
320
a6f24a5b 321#ifdef GIT_SSL
66024c7c
CMN
322 if (t->encrypt)
323 return send_ssl(&t->ssl, msg, len);
324#endif
325
326 while (off < len) {
327 errno = 0;
328 ret = p_send(t->socket, msg + off, len - off, flags);
56b7df10 329 if (ret < 0) {
bd6585a7 330 net_set_error("Error sending data");
56b7df10
CMN
331 return -1;
332 }
4e95ef02
CMN
333
334 off += ret;
335 }
336
44ef8b1b 337 return (int)off;
4e95ef02 338}
74bd343a 339
34bfb4b0 340
bad53552
CMN
341#ifdef GIT_WIN32
342int gitno_close(GIT_SOCKET s)
343{
344 return closesocket(s) == SOCKET_ERROR ? -1 : 0;
345}
346#else
347int gitno_close(GIT_SOCKET s)
348{
349 return close(s);
350}
351#endif
352
74bd343a
CMN
353int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
354{
355 fd_set fds;
356 struct timeval tv;
357
358 tv.tv_sec = sec;
359 tv.tv_usec = usec;
360
361 FD_ZERO(&fds);
362 FD_SET(buf->fd, &fds);
363
364 /* The select(2) interface is silly */
44ef8b1b 365 return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);
74bd343a 366}
db84b798
CMN
367
368int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
369{
370 char *colon, *slash, *delim;
db84b798
CMN
371
372 colon = strchr(url, ':');
373 slash = strchr(url, '/');
374
56b7df10 375 if (slash == NULL) {
44ef8b1b
RB
376 giterr_set(GITERR_NET, "Malformed URL: missing /");
377 return -1;
56b7df10 378 }
db84b798
CMN
379
380 if (colon == NULL) {
381 *port = git__strdup(default_port);
382 } else {
383 *port = git__strndup(colon + 1, slash - colon - 1);
384 }
56b7df10 385 GITERR_CHECK_ALLOC(*port);
db84b798
CMN
386
387 delim = colon == NULL ? slash : colon;
388 *host = git__strndup(url, delim - url);
56b7df10 389 GITERR_CHECK_ALLOC(*host);
db84b798 390
56b7df10 391 return 0;
db84b798 392}