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