diff options
Diffstat (limited to 'src/transports/smart.c')
-rw-r--r-- | src/transports/smart.c | 560 |
1 files changed, 0 insertions, 560 deletions
diff --git a/src/transports/smart.c b/src/transports/smart.c deleted file mode 100644 index 587f14358..000000000 --- a/src/transports/smart.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "smart.h" - -#include "git2.h" -#include "refs.h" -#include "refspec.h" -#include "proxy.h" - -static int git_smart__recv_cb(gitno_buffer *buf) -{ - transport_smart *t = (transport_smart *) buf->cb_data; - size_t old_len, bytes_read; - int error; - - GIT_ASSERT(t->current_stream); - - old_len = buf->offset; - - if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0) - return error; - - buf->offset += bytes_read; - - if (t->packetsize_cb && !t->cancelled.val) { - error = t->packetsize_cb(bytes_read, t->packetsize_payload); - if (error) { - git_atomic32_set(&t->cancelled, 1); - return GIT_EUSER; - } - } - - return (int)(buf->offset - old_len); -} - -GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport) -{ - if (t->current_stream) { - t->current_stream->free(t->current_stream); - t->current_stream = NULL; - } - - if (close_subtransport) { - git__free(t->url); - t->url = NULL; - - if (t->wrapped->close(t->wrapped) < 0) - return -1; - } - - return 0; -} - -static int git_smart__set_callbacks( - git_transport *transport, - git_transport_message_cb progress_cb, - git_transport_message_cb error_cb, - git_transport_certificate_check_cb certificate_check_cb, - void *message_cb_payload) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - t->progress_cb = progress_cb; - t->error_cb = error_cb; - t->certificate_check_cb = certificate_check_cb; - t->message_cb_payload = message_cb_payload; - - return 0; -} - -static size_t http_header_name_length(const char *http_header) -{ - const char *colon = strchr(http_header, ':'); - if (!colon) - return 0; - return colon - http_header; -} - -static bool is_malformed_http_header(const char *http_header) -{ - const char *c; - size_t name_len; - - /* Disallow \r and \n */ - c = strchr(http_header, '\r'); - if (c) - return true; - c = strchr(http_header, '\n'); - if (c) - return true; - - /* Require a header name followed by : */ - name_len = http_header_name_length(http_header); - if (name_len < 1) - return true; - - return false; -} - -static char *forbidden_custom_headers[] = { - "User-Agent", - "Host", - "Accept", - "Content-Type", - "Transfer-Encoding", - "Content-Length", -}; - -static bool is_forbidden_custom_header(const char *custom_header) -{ - unsigned long i; - size_t name_len = http_header_name_length(custom_header); - - /* Disallow headers that we set */ - for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++) - if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0) - return true; - - return false; -} - -static int git_smart__set_custom_headers( - git_transport *transport, - const git_strarray *custom_headers) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - size_t i; - - if (t->custom_headers.count) - git_strarray_dispose(&t->custom_headers); - - if (!custom_headers) - return 0; - - for (i = 0; i < custom_headers->count; i++) { - if (is_malformed_http_header(custom_headers->strings[i])) { - git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]); - return -1; - } - if (is_forbidden_custom_header(custom_headers->strings[i])) { - git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]); - return -1; - } - } - - return git_strarray_copy(&t->custom_headers, custom_headers); -} - -int git_smart__update_heads(transport_smart *t, git_vector *symrefs) -{ - size_t i; - git_pkt *pkt; - - git_vector_clear(&t->heads); - git_vector_foreach(&t->refs, i, pkt) { - git_pkt_ref *ref = (git_pkt_ref *) pkt; - if (pkt->type != GIT_PKT_REF) - continue; - - if (symrefs) { - git_refspec *spec; - git_buf buf = GIT_BUF_INIT; - size_t j; - int error = 0; - - git_vector_foreach(symrefs, j, spec) { - git_buf_clear(&buf); - if (git_refspec_src_matches(spec, ref->head.name) && - !(error = git_refspec_transform(&buf, spec, ref->head.name))) { - git__free(ref->head.symref_target); - ref->head.symref_target = git_buf_detach(&buf); - } - } - - git_buf_dispose(&buf); - - if (error < 0) - return error; - } - - if (git_vector_insert(&t->heads, &ref->head) < 0) - return -1; - } - - return 0; -} - -static void free_symrefs(git_vector *symrefs) -{ - git_refspec *spec; - size_t i; - - git_vector_foreach(symrefs, i, spec) { - git_refspec__dispose(spec); - git__free(spec); - } - - git_vector_free(symrefs); -} - -static int git_smart__connect( - git_transport *transport, - const char *url, - git_credential_acquire_cb cred_acquire_cb, - void *cred_acquire_payload, - const git_proxy_options *proxy, - int direction, - int flags) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - git_smart_subtransport_stream *stream; - int error; - git_pkt *pkt; - git_pkt_ref *first; - git_vector symrefs; - git_smart_service_t service; - - if (git_smart__reset_stream(t, true) < 0) - return -1; - - t->url = git__strdup(url); - GIT_ERROR_CHECK_ALLOC(t->url); - - git_proxy_options_clear(&t->proxy); - - if (git_proxy_options_dup(&t->proxy, proxy) < 0) - return -1; - - t->direction = direction; - t->flags = flags; - t->cred_acquire_cb = cred_acquire_cb; - t->cred_acquire_payload = cred_acquire_payload; - - if (GIT_DIRECTION_FETCH == t->direction) - service = GIT_SERVICE_UPLOADPACK_LS; - else if (GIT_DIRECTION_PUSH == t->direction) - service = GIT_SERVICE_RECEIVEPACK_LS; - else { - git_error_set(GIT_ERROR_NET, "invalid direction"); - return -1; - } - - if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0) - return error; - - /* Save off the current stream (i.e. socket) that we are working with */ - t->current_stream = stream; - - gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - - /* 2 flushes for RPC; 1 for stateful */ - if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) - return error; - - /* Strip the comment packet for RPC */ - if (t->rpc) { - pkt = (git_pkt *)git_vector_get(&t->refs, 0); - - if (!pkt || GIT_PKT_COMMENT != pkt->type) { - git_error_set(GIT_ERROR_NET, "invalid response"); - return -1; - } else { - /* Remove the comment pkt from the list */ - git_vector_remove(&t->refs, 0); - git__free(pkt); - } - } - - /* We now have loaded the refs. */ - t->have_refs = 1; - - pkt = (git_pkt *)git_vector_get(&t->refs, 0); - if (pkt && GIT_PKT_REF != pkt->type) { - git_error_set(GIT_ERROR_NET, "invalid response"); - return -1; - } - first = (git_pkt_ref *)pkt; - - if ((error = git_vector_init(&symrefs, 1, NULL)) < 0) - return error; - - /* Detect capabilities */ - if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) { - /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */ - if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") && - git_oid_is_zero(&first->head.oid)) { - git_vector_clear(&t->refs); - git_pkt_free((git_pkt *)first); - } - - /* Keep a list of heads for _ls */ - git_smart__update_heads(t, &symrefs); - } else if (error == GIT_ENOTFOUND) { - /* There was no ref packet received, or the cap list was empty */ - error = 0; - } else { - git_error_set(GIT_ERROR_NET, "invalid response"); - goto cleanup; - } - - if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0) - goto cleanup; - - /* We're now logically connected. */ - t->connected = 1; - -cleanup: - free_symrefs(&symrefs); - - return error; -} - -static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - if (!t->have_refs) { - git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs"); - return -1; - } - - *out = (const git_remote_head **) t->heads.contents; - *size = t->heads.length; - - return 0; -} - -int git_smart__negotiation_step(git_transport *transport, void *data, size_t len) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - git_smart_subtransport_stream *stream; - int error; - - if (t->rpc && git_smart__reset_stream(t, false) < 0) - return -1; - - if (GIT_DIRECTION_FETCH != t->direction) { - git_error_set(GIT_ERROR_NET, "this operation is only valid for fetch"); - return -1; - } - - if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0) - return error; - - /* If this is a stateful implementation, the stream we get back should be the same */ - GIT_ASSERT(t->rpc || t->current_stream == stream); - - /* Save off the current stream (i.e. socket) that we are working with */ - t->current_stream = stream; - - if ((error = stream->write(stream, (const char *)data, len)) < 0) - return error; - - gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - - return 0; -} - -int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream) -{ - int error; - - if (t->rpc && git_smart__reset_stream(t, false) < 0) - return -1; - - if (GIT_DIRECTION_PUSH != t->direction) { - git_error_set(GIT_ERROR_NET, "this operation is only valid for push"); - return -1; - } - - if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0) - return error; - - /* If this is a stateful implementation, the stream we get back should be the same */ - GIT_ASSERT(t->rpc || t->current_stream == *stream); - - /* Save off the current stream (i.e. socket) that we are working with */ - t->current_stream = *stream; - - gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); - - return 0; -} - -static void git_smart__cancel(git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - git_atomic32_set(&t->cancelled, 1); -} - -static int git_smart__is_connected(git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - return t->connected; -} - -static int git_smart__read_flags(git_transport *transport, int *flags) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - *flags = t->flags; - - return 0; -} - -static int git_smart__close(git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - git_vector *common = &t->common; - unsigned int i; - git_pkt *p; - int ret; - git_smart_subtransport_stream *stream; - const char flush[] = "0000"; - - /* - * If we're still connected at this point and not using RPC, - * we should say goodbye by sending a flush, or git-daemon - * will complain that we disconnected unexpectedly. - */ - if (t->connected && !t->rpc && - !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) { - t->current_stream->write(t->current_stream, flush, 4); - } - - ret = git_smart__reset_stream(t, true); - - git_vector_foreach(common, i, p) - git_pkt_free(p); - - git_vector_free(common); - - if (t->url) { - git__free(t->url); - t->url = NULL; - } - - t->connected = 0; - - return ret; -} - -static void git_smart__free(git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - git_vector *refs = &t->refs; - unsigned int i; - git_pkt *p; - - /* Make sure that the current stream is closed, if we have one. */ - git_smart__close(transport); - - /* Free the subtransport */ - t->wrapped->free(t->wrapped); - - git_vector_free(&t->heads); - git_vector_foreach(refs, i, p) - git_pkt_free(p); - - git_vector_free(refs); - git__free((char *)t->proxy.url); - - git_strarray_dispose(&t->custom_headers); - - git__free(t); -} - -static int ref_name_cmp(const void *a, const void *b) -{ - const git_pkt_ref *ref_a = a, *ref_b = b; - - return strcmp(ref_a->head.name, ref_b->head.name); -} - -int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - GIT_ASSERT_ARG(transport); - GIT_ASSERT_ARG(cert); - GIT_ASSERT_ARG(hostname); - - if (!t->certificate_check_cb) - return GIT_PASSTHROUGH; - - return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload); -} - -int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(transport); - - if (!t->cred_acquire_cb) - return GIT_PASSTHROUGH; - - return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); -} - -int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport) -{ - transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); - return git_proxy_options_dup(out, &t->proxy); -} - -int git_transport_smart(git_transport **out, git_remote *owner, void *param) -{ - transport_smart *t; - git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param; - - if (!param) - return -1; - - t = git__calloc(1, sizeof(transport_smart)); - GIT_ERROR_CHECK_ALLOC(t); - - t->parent.version = GIT_TRANSPORT_VERSION; - t->parent.set_callbacks = git_smart__set_callbacks; - t->parent.set_custom_headers = git_smart__set_custom_headers; - t->parent.connect = git_smart__connect; - t->parent.close = git_smart__close; - t->parent.free = git_smart__free; - t->parent.negotiate_fetch = git_smart__negotiate_fetch; - t->parent.download_pack = git_smart__download_pack; - t->parent.push = git_smart__push; - t->parent.ls = git_smart__ls; - t->parent.is_connected = git_smart__is_connected; - t->parent.read_flags = git_smart__read_flags; - t->parent.cancel = git_smart__cancel; - - t->owner = owner; - t->rpc = definition->rpc; - - if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) { - git__free(t); - return -1; - } - - if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) { - git__free(t); - return -1; - } - - if (definition->callback(&t->wrapped, &t->parent, definition->param) < 0) { - git__free(t); - return -1; - } - - *out = (git_transport *) t; - return 0; -} |