]> git.proxmox.com Git - libgit2.git/blame - src/curl_stream.c
Include stacktrace summary in memory leak output.
[libgit2.git] / src / curl_stream.c
CommitLineData
8dea1c21
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
8#ifdef GIT_CURL
9
10#include <curl/curl.h>
11
12#include "stream.h"
13#include "git2/transport.h"
14#include "buffer.h"
cdee630f 15#include "vector.h"
8dea1c21
CMN
16
17typedef struct {
18 git_stream parent;
19 CURL *handle;
20 curl_socket_t socket;
21 char curl_error[CURL_ERROR_SIZE + 1];
22 git_cert_x509 cert_info;
cdee630f 23 git_strarray cert_info_strings;
8dea1c21
CMN
24} curl_stream;
25
26static int seterr_curl(curl_stream *s)
27{
28 giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
29 return -1;
30}
31
32static int curls_connect(git_stream *stream)
33{
34 curl_stream *s = (curl_stream *) stream;
35 long sockextr;
36 int failed_cert = 0;
37 CURLcode res;
38 res = curl_easy_perform(s->handle);
39
40 if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
41 return seterr_curl(s);
42 if (res == CURLE_PEER_FAILED_VERIFICATION)
43 failed_cert = 1;
44
45 if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK)
46 return seterr_curl(s);
47
48 s->socket = sockextr;
49
50 if (s->parent.encrypted && failed_cert)
51 return GIT_ECERTIFICATE;
52
53 return 0;
54}
55
56static int curls_certificate(git_cert **out, git_stream *stream)
57{
cdee630f
CMN
58 int error;
59 CURLcode res;
60 struct curl_slist *slist;
61 struct curl_certinfo *certinfo;
62 git_vector strings = GIT_VECTOR_INIT;
8dea1c21
CMN
63 curl_stream *s = (curl_stream *) stream;
64
cdee630f
CMN
65 if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
66 return seterr_curl(s);
67
68 /* No information is available, can happen with SecureTransport */
69 if (certinfo->num_of_certs == 0) {
70 s->cert_info.cert_type = GIT_CERT_NONE;
71 s->cert_info.data = NULL;
72 s->cert_info.len = 0;
73 return 0;
74 }
75
76 if ((error = git_vector_init(&strings, 8, NULL)) < 0)
77 return error;
78
79 for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
80 char *str = git__strdup(slist->data);
81 GITERR_CHECK_ALLOC(str);
82 }
83
84 /* Copy the contents of the vector into a strarray so we can expose them */
85 s->cert_info_strings.strings = (char **) strings.contents;
86 s->cert_info_strings.count = strings.length;
87
88 s->cert_info.cert_type = GIT_CERT_STRARRAY;
89 s->cert_info.data = &s->cert_info_strings;
90 s->cert_info.len = strings.length;
8dea1c21
CMN
91
92 *out = (git_cert *) &s->cert_info;
93
94 return 0;
95}
96
1376e784
CMN
97static int curls_set_proxy(git_stream *stream, const char *proxy_url)
98{
99 CURLcode res;
100 curl_stream *s = (curl_stream *) stream;
101
102 if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK)
103 return seterr_curl(s);
104
105 return 0;
106}
107
8dea1c21
CMN
108static int wait_for(curl_socket_t fd, bool reading)
109{
110 int ret;
111 fd_set infd, outfd, errfd;
112
113 FD_ZERO(&infd);
114 FD_ZERO(&outfd);
115 FD_ZERO(&errfd);
116
117 FD_SET(fd, &errfd);
118 if (reading)
119 FD_SET(fd, &infd);
120 else
121 FD_SET(fd, &outfd);
122
123 if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
124 giterr_set(GITERR_OS, "error in select");
125 return -1;
126 }
127
128 return 0;
129}
130
131static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
132{
133 int error;
134 size_t off = 0, sent;
135 CURLcode res;
136 curl_stream *s = (curl_stream *) stream;
137
138 GIT_UNUSED(flags);
139
140 do {
141 if ((error = wait_for(s->socket, false)) < 0)
142 return error;
143
144 res = curl_easy_send(s->handle, data + off, len - off, &sent);
145 if (res == CURLE_OK)
146 off += sent;
147 } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
148
149 if (res != CURLE_OK)
150 return seterr_curl(s);
151
152 return len;
153}
154
155static ssize_t curls_read(git_stream *stream, void *data, size_t len)
156{
157 int error;
158 size_t read;
159 CURLcode res;
160 curl_stream *s = (curl_stream *) stream;
161
162 do {
163 if ((error = wait_for(s->socket, true)) < 0)
164 return error;
165
166 res = curl_easy_recv(s->handle, data, len, &read);
167 } while (res == CURLE_AGAIN);
168
169 if (res != CURLE_OK)
170 return seterr_curl(s);
171
172 return read;
173}
174
175static int curls_close(git_stream *stream)
176{
177 curl_stream *s = (curl_stream *) stream;
178
179 if (!s->handle)
180 return 0;
181
182 curl_easy_cleanup(s->handle);
183 s->handle = NULL;
184 s->socket = 0;
185
186 return 0;
187}
188
189static void curls_free(git_stream *stream)
190{
191 curl_stream *s = (curl_stream *) stream;
192
193 curls_close(stream);
cdee630f 194 git_strarray_free(&s->cert_info_strings);
8dea1c21
CMN
195 git__free(s);
196}
197
8443f492 198int git_curl_stream_new(git_stream **out, const char *host, const char *port)
8dea1c21
CMN
199{
200 curl_stream *st;
201 CURL *handle;
202 int iport = 0, error;
203
204 st = git__calloc(1, sizeof(curl_stream));
205 GITERR_CHECK_ALLOC(st);
206
207 handle = curl_easy_init();
208 if (handle == NULL) {
209 giterr_set(GITERR_NET, "failed to create curl handle");
210 return -1;
211 }
212
213 if ((error = git__strtol32(&iport, port, NULL, 10)) < 0)
214 return error;
215
8443f492 216 curl_easy_setopt(handle, CURLOPT_URL, host);
8dea1c21
CMN
217 curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
218 curl_easy_setopt(handle, CURLOPT_PORT, iport);
219 curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
220 curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
221 curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
1376e784
CMN
222 curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
223
8dea1c21
CMN
224 /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
225
226 st->parent.version = GIT_STREAM_VERSION;
8443f492 227 st->parent.encrypted = 0; /* we don't encrypt ourselves */
1376e784 228 st->parent.proxy_support = 1;
8dea1c21
CMN
229 st->parent.connect = curls_connect;
230 st->parent.certificate = curls_certificate;
1376e784 231 st->parent.set_proxy = curls_set_proxy;
8dea1c21
CMN
232 st->parent.read = curls_read;
233 st->parent.write = curls_write;
234 st->parent.close = curls_close;
235 st->parent.free = curls_free;
236 st->handle = handle;
237
238 *out = (git_stream *) st;
239 return 0;
240}
241
242#else
243
244#include "stream.h"
245
246int git_curl_stream_new(git_stream **out, const char *host, const char *port)
247{
248 GIT_UNUSED(out);
249 GIT_UNUSED(host);
250 GIT_UNUSED(port);
251
252 giterr_set(GITERR_NET, "curl is not supported in this version");
253 return -1;
254}
255
256
257#endif