]>
Commit | Line | Data |
---|---|---|
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 | |
17 | typedef 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 | ||
26 | static int seterr_curl(curl_stream *s) | |
27 | { | |
28 | giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error); | |
29 | return -1; | |
30 | } | |
31 | ||
32 | static 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 | ||
56 | static 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 |
97 | static 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 |
108 | static 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 | ||
131 | static 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 | ||
155 | static 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 | ||
175 | static 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 | ||
189 | static 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 | 198 | int 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 | ||
246 | int 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 |