]> git.proxmox.com Git - libgit2.git/blob - src/transports/smart.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / transports / smart.c
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 #include "smart.h"
9
10 #include "git2.h"
11 #include "refs.h"
12 #include "refspec.h"
13 #include "proxy.h"
14
15 static int git_smart__recv_cb(gitno_buffer *buf)
16 {
17 transport_smart *t = (transport_smart *) buf->cb_data;
18 size_t old_len, bytes_read;
19 int error;
20
21 assert(t->current_stream);
22
23 old_len = buf->offset;
24
25 if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0)
26 return error;
27
28 buf->offset += bytes_read;
29
30 if (t->packetsize_cb && !t->cancelled.val) {
31 error = t->packetsize_cb(bytes_read, t->packetsize_payload);
32 if (error) {
33 git_atomic_set(&t->cancelled, 1);
34 return GIT_EUSER;
35 }
36 }
37
38 return (int)(buf->offset - old_len);
39 }
40
41 GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport)
42 {
43 if (t->current_stream) {
44 t->current_stream->free(t->current_stream);
45 t->current_stream = NULL;
46 }
47
48 if (close_subtransport) {
49 git__free(t->url);
50 t->url = NULL;
51
52 if (t->wrapped->close(t->wrapped) < 0)
53 return -1;
54 }
55
56 return 0;
57 }
58
59 static int git_smart__set_callbacks(
60 git_transport *transport,
61 git_transport_message_cb progress_cb,
62 git_transport_message_cb error_cb,
63 git_transport_certificate_check_cb certificate_check_cb,
64 void *message_cb_payload)
65 {
66 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
67
68 t->progress_cb = progress_cb;
69 t->error_cb = error_cb;
70 t->certificate_check_cb = certificate_check_cb;
71 t->message_cb_payload = message_cb_payload;
72
73 return 0;
74 }
75
76 static size_t http_header_name_length(const char *http_header)
77 {
78 const char *colon = strchr(http_header, ':');
79 if (!colon)
80 return 0;
81 return colon - http_header;
82 }
83
84 static bool is_malformed_http_header(const char *http_header)
85 {
86 const char *c;
87 size_t name_len;
88
89 /* Disallow \r and \n */
90 c = strchr(http_header, '\r');
91 if (c)
92 return true;
93 c = strchr(http_header, '\n');
94 if (c)
95 return true;
96
97 /* Require a header name followed by : */
98 name_len = http_header_name_length(http_header);
99 if (name_len < 1)
100 return true;
101
102 return false;
103 }
104
105 static char *forbidden_custom_headers[] = {
106 "User-Agent",
107 "Host",
108 "Accept",
109 "Content-Type",
110 "Transfer-Encoding",
111 "Content-Length",
112 };
113
114 static bool is_forbidden_custom_header(const char *custom_header)
115 {
116 unsigned long i;
117 size_t name_len = http_header_name_length(custom_header);
118
119 /* Disallow headers that we set */
120 for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
121 if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
122 return true;
123
124 return false;
125 }
126
127 static int git_smart__set_custom_headers(
128 git_transport *transport,
129 const git_strarray *custom_headers)
130 {
131 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
132 size_t i;
133
134 if (t->custom_headers.count)
135 git_strarray_dispose(&t->custom_headers);
136
137 if (!custom_headers)
138 return 0;
139
140 for (i = 0; i < custom_headers->count; i++) {
141 if (is_malformed_http_header(custom_headers->strings[i])) {
142 git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
143 return -1;
144 }
145 if (is_forbidden_custom_header(custom_headers->strings[i])) {
146 git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
147 return -1;
148 }
149 }
150
151 return git_strarray_copy(&t->custom_headers, custom_headers);
152 }
153
154 int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
155 {
156 size_t i;
157 git_pkt *pkt;
158
159 git_vector_clear(&t->heads);
160 git_vector_foreach(&t->refs, i, pkt) {
161 git_pkt_ref *ref = (git_pkt_ref *) pkt;
162 if (pkt->type != GIT_PKT_REF)
163 continue;
164
165 if (symrefs) {
166 git_refspec *spec;
167 git_buf buf = GIT_BUF_INIT;
168 size_t j;
169 int error = 0;
170
171 git_vector_foreach(symrefs, j, spec) {
172 git_buf_clear(&buf);
173 if (git_refspec_src_matches(spec, ref->head.name) &&
174 !(error = git_refspec_transform(&buf, spec, ref->head.name))) {
175 git__free(ref->head.symref_target);
176 ref->head.symref_target = git_buf_detach(&buf);
177 }
178 }
179
180 git_buf_dispose(&buf);
181
182 if (error < 0)
183 return error;
184 }
185
186 if (git_vector_insert(&t->heads, &ref->head) < 0)
187 return -1;
188 }
189
190 return 0;
191 }
192
193 static void free_symrefs(git_vector *symrefs)
194 {
195 git_refspec *spec;
196 size_t i;
197
198 git_vector_foreach(symrefs, i, spec) {
199 git_refspec__dispose(spec);
200 git__free(spec);
201 }
202
203 git_vector_free(symrefs);
204 }
205
206 static int git_smart__connect(
207 git_transport *transport,
208 const char *url,
209 git_credential_acquire_cb cred_acquire_cb,
210 void *cred_acquire_payload,
211 const git_proxy_options *proxy,
212 int direction,
213 int flags)
214 {
215 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
216 git_smart_subtransport_stream *stream;
217 int error;
218 git_pkt *pkt;
219 git_pkt_ref *first;
220 git_vector symrefs;
221 git_smart_service_t service;
222
223 if (git_smart__reset_stream(t, true) < 0)
224 return -1;
225
226 t->url = git__strdup(url);
227 GIT_ERROR_CHECK_ALLOC(t->url);
228
229 if (git_proxy_options_dup(&t->proxy, proxy) < 0)
230 return -1;
231
232 t->direction = direction;
233 t->flags = flags;
234 t->cred_acquire_cb = cred_acquire_cb;
235 t->cred_acquire_payload = cred_acquire_payload;
236
237 if (GIT_DIRECTION_FETCH == t->direction)
238 service = GIT_SERVICE_UPLOADPACK_LS;
239 else if (GIT_DIRECTION_PUSH == t->direction)
240 service = GIT_SERVICE_RECEIVEPACK_LS;
241 else {
242 git_error_set(GIT_ERROR_NET, "invalid direction");
243 return -1;
244 }
245
246 if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0)
247 return error;
248
249 /* Save off the current stream (i.e. socket) that we are working with */
250 t->current_stream = stream;
251
252 gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
253
254 /* 2 flushes for RPC; 1 for stateful */
255 if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
256 return error;
257
258 /* Strip the comment packet for RPC */
259 if (t->rpc) {
260 pkt = (git_pkt *)git_vector_get(&t->refs, 0);
261
262 if (!pkt || GIT_PKT_COMMENT != pkt->type) {
263 git_error_set(GIT_ERROR_NET, "invalid response");
264 return -1;
265 } else {
266 /* Remove the comment pkt from the list */
267 git_vector_remove(&t->refs, 0);
268 git__free(pkt);
269 }
270 }
271
272 /* We now have loaded the refs. */
273 t->have_refs = 1;
274
275 pkt = (git_pkt *)git_vector_get(&t->refs, 0);
276 if (pkt && GIT_PKT_REF != pkt->type) {
277 git_error_set(GIT_ERROR_NET, "invalid response");
278 return -1;
279 }
280 first = (git_pkt_ref *)pkt;
281
282 if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
283 return error;
284
285 /* Detect capabilities */
286 if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) {
287 /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
288 if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
289 git_oid_is_zero(&first->head.oid)) {
290 git_vector_clear(&t->refs);
291 git_pkt_free((git_pkt *)first);
292 }
293
294 /* Keep a list of heads for _ls */
295 git_smart__update_heads(t, &symrefs);
296 } else if (error == GIT_ENOTFOUND) {
297 /* There was no ref packet received, or the cap list was empty */
298 error = 0;
299 } else {
300 git_error_set(GIT_ERROR_NET, "invalid response");
301 goto cleanup;
302 }
303
304 if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0)
305 goto cleanup;
306
307 /* We're now logically connected. */
308 t->connected = 1;
309
310 cleanup:
311 free_symrefs(&symrefs);
312
313 return error;
314 }
315
316 static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
317 {
318 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
319
320 if (!t->have_refs) {
321 git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
322 return -1;
323 }
324
325 *out = (const git_remote_head **) t->heads.contents;
326 *size = t->heads.length;
327
328 return 0;
329 }
330
331 int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
332 {
333 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
334 git_smart_subtransport_stream *stream;
335 int error;
336
337 if (t->rpc && git_smart__reset_stream(t, false) < 0)
338 return -1;
339
340 if (GIT_DIRECTION_FETCH != t->direction) {
341 git_error_set(GIT_ERROR_NET, "this operation is only valid for fetch");
342 return -1;
343 }
344
345 if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0)
346 return error;
347
348 /* If this is a stateful implementation, the stream we get back should be the same */
349 assert(t->rpc || t->current_stream == stream);
350
351 /* Save off the current stream (i.e. socket) that we are working with */
352 t->current_stream = stream;
353
354 if ((error = stream->write(stream, (const char *)data, len)) < 0)
355 return error;
356
357 gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
358
359 return 0;
360 }
361
362 int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream)
363 {
364 int error;
365
366 if (t->rpc && git_smart__reset_stream(t, false) < 0)
367 return -1;
368
369 if (GIT_DIRECTION_PUSH != t->direction) {
370 git_error_set(GIT_ERROR_NET, "this operation is only valid for push");
371 return -1;
372 }
373
374 if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0)
375 return error;
376
377 /* If this is a stateful implementation, the stream we get back should be the same */
378 assert(t->rpc || t->current_stream == *stream);
379
380 /* Save off the current stream (i.e. socket) that we are working with */
381 t->current_stream = *stream;
382
383 gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
384
385 return 0;
386 }
387
388 static void git_smart__cancel(git_transport *transport)
389 {
390 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
391
392 git_atomic_set(&t->cancelled, 1);
393 }
394
395 static int git_smart__is_connected(git_transport *transport)
396 {
397 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
398
399 return t->connected;
400 }
401
402 static int git_smart__read_flags(git_transport *transport, int *flags)
403 {
404 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
405
406 *flags = t->flags;
407
408 return 0;
409 }
410
411 static int git_smart__close(git_transport *transport)
412 {
413 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
414 git_vector *common = &t->common;
415 unsigned int i;
416 git_pkt *p;
417 int ret;
418 git_smart_subtransport_stream *stream;
419 const char flush[] = "0000";
420
421 /*
422 * If we're still connected at this point and not using RPC,
423 * we should say goodbye by sending a flush, or git-daemon
424 * will complain that we disconnected unexpectedly.
425 */
426 if (t->connected && !t->rpc &&
427 !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
428 t->current_stream->write(t->current_stream, flush, 4);
429 }
430
431 ret = git_smart__reset_stream(t, true);
432
433 git_vector_foreach(common, i, p)
434 git_pkt_free(p);
435
436 git_vector_free(common);
437
438 if (t->url) {
439 git__free(t->url);
440 t->url = NULL;
441 }
442
443 t->connected = 0;
444
445 return ret;
446 }
447
448 static void git_smart__free(git_transport *transport)
449 {
450 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
451 git_vector *refs = &t->refs;
452 unsigned int i;
453 git_pkt *p;
454
455 /* Make sure that the current stream is closed, if we have one. */
456 git_smart__close(transport);
457
458 /* Free the subtransport */
459 t->wrapped->free(t->wrapped);
460
461 git_vector_free(&t->heads);
462 git_vector_foreach(refs, i, p)
463 git_pkt_free(p);
464
465 git_vector_free(refs);
466 git__free((char *)t->proxy.url);
467
468 git_strarray_dispose(&t->custom_headers);
469
470 git__free(t);
471 }
472
473 static int ref_name_cmp(const void *a, const void *b)
474 {
475 const git_pkt_ref *ref_a = a, *ref_b = b;
476
477 return strcmp(ref_a->head.name, ref_b->head.name);
478 }
479
480 int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
481 {
482 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
483
484 assert(transport && cert && hostname);
485
486 if (!t->certificate_check_cb)
487 return GIT_PASSTHROUGH;
488
489 return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload);
490 }
491
492 int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods)
493 {
494 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
495
496 assert(out && transport);
497
498 if (!t->cred_acquire_cb)
499 return GIT_PASSTHROUGH;
500
501 return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
502 }
503
504 int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport)
505 {
506 transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
507 return git_proxy_options_dup(out, &t->proxy);
508 }
509
510 int git_transport_smart(git_transport **out, git_remote *owner, void *param)
511 {
512 transport_smart *t;
513 git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param;
514
515 if (!param)
516 return -1;
517
518 t = git__calloc(1, sizeof(transport_smart));
519 GIT_ERROR_CHECK_ALLOC(t);
520
521 t->parent.version = GIT_TRANSPORT_VERSION;
522 t->parent.set_callbacks = git_smart__set_callbacks;
523 t->parent.set_custom_headers = git_smart__set_custom_headers;
524 t->parent.connect = git_smart__connect;
525 t->parent.close = git_smart__close;
526 t->parent.free = git_smart__free;
527 t->parent.negotiate_fetch = git_smart__negotiate_fetch;
528 t->parent.download_pack = git_smart__download_pack;
529 t->parent.push = git_smart__push;
530 t->parent.ls = git_smart__ls;
531 t->parent.is_connected = git_smart__is_connected;
532 t->parent.read_flags = git_smart__read_flags;
533 t->parent.cancel = git_smart__cancel;
534
535 t->owner = owner;
536 t->rpc = definition->rpc;
537
538 if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) {
539 git__free(t);
540 return -1;
541 }
542
543 if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
544 git__free(t);
545 return -1;
546 }
547
548 if (definition->callback(&t->wrapped, &t->parent, definition->param) < 0) {
549 git__free(t);
550 return -1;
551 }
552
553 *out = (git_transport *) t;
554 return 0;
555 }