diff options
Diffstat (limited to 'src/net.c')
-rw-r--r-- | src/net.c | 540 |
1 files changed, 0 insertions, 540 deletions
diff --git a/src/net.c b/src/net.c deleted file mode 100644 index 361e40e7b..000000000 --- a/src/net.c +++ /dev/null @@ -1,540 +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 "net.h" -#include "netops.h" - -#include <ctype.h> -#include "git2/errors.h" - -#include "posix.h" -#include "buffer.h" -#include "http_parser.h" -#include "runtime.h" - -#define DEFAULT_PORT_HTTP "80" -#define DEFAULT_PORT_HTTPS "443" -#define DEFAULT_PORT_GIT "9418" -#define DEFAULT_PORT_SSH "22" - -static const char *default_port_for_scheme(const char *scheme) -{ - if (strcmp(scheme, "http") == 0) - return DEFAULT_PORT_HTTP; - else if (strcmp(scheme, "https") == 0) - return DEFAULT_PORT_HTTPS; - else if (strcmp(scheme, "git") == 0) - return DEFAULT_PORT_GIT; - else if (strcmp(scheme, "ssh") == 0) - return DEFAULT_PORT_SSH; - - return NULL; -} - -int git_net_url_dup(git_net_url *out, git_net_url *in) -{ - if (in->scheme) { - out->scheme = git__strdup(in->scheme); - GIT_ERROR_CHECK_ALLOC(out->scheme); - } - - if (in->host) { - out->host = git__strdup(in->host); - GIT_ERROR_CHECK_ALLOC(out->host); - } - - if (in->port) { - out->port = git__strdup(in->port); - GIT_ERROR_CHECK_ALLOC(out->port); - } - - if (in->path) { - out->path = git__strdup(in->path); - GIT_ERROR_CHECK_ALLOC(out->path); - } - - if (in->query) { - out->query = git__strdup(in->query); - GIT_ERROR_CHECK_ALLOC(out->query); - } - - if (in->username) { - out->username = git__strdup(in->username); - GIT_ERROR_CHECK_ALLOC(out->username); - } - - if (in->password) { - out->password = git__strdup(in->password); - GIT_ERROR_CHECK_ALLOC(out->password); - } - - return 0; -} - -int git_net_url_parse(git_net_url *url, const char *given) -{ - struct http_parser_url u = {0}; - bool has_scheme, has_host, has_port, has_path, has_query, has_userinfo; - git_buf scheme = GIT_BUF_INIT, - host = GIT_BUF_INIT, - port = GIT_BUF_INIT, - path = GIT_BUF_INIT, - username = GIT_BUF_INIT, - password = GIT_BUF_INIT, - query = GIT_BUF_INIT; - int error = GIT_EINVALIDSPEC; - - if (http_parser_parse_url(given, strlen(given), false, &u)) { - git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given); - goto done; - } - - has_scheme = !!(u.field_set & (1 << UF_SCHEMA)); - has_host = !!(u.field_set & (1 << UF_HOST)); - has_port = !!(u.field_set & (1 << UF_PORT)); - has_path = !!(u.field_set & (1 << UF_PATH)); - has_query = !!(u.field_set & (1 << UF_QUERY)); - has_userinfo = !!(u.field_set & (1 << UF_USERINFO)); - - if (has_scheme) { - const char *url_scheme = given + u.field_data[UF_SCHEMA].off; - size_t url_scheme_len = u.field_data[UF_SCHEMA].len; - git_buf_put(&scheme, url_scheme, url_scheme_len); - git__strntolower(scheme.ptr, scheme.size); - } else { - git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given); - goto done; - } - - if (has_host) { - const char *url_host = given + u.field_data[UF_HOST].off; - size_t url_host_len = u.field_data[UF_HOST].len; - git_buf_decode_percent(&host, url_host, url_host_len); - } - - if (has_port) { - const char *url_port = given + u.field_data[UF_PORT].off; - size_t url_port_len = u.field_data[UF_PORT].len; - git_buf_put(&port, url_port, url_port_len); - } else { - const char *default_port = default_port_for_scheme(scheme.ptr); - - if (default_port == NULL) { - git_error_set(GIT_ERROR_NET, "unknown scheme for URL '%s'", given); - goto done; - } - - git_buf_puts(&port, default_port); - } - - if (has_path) { - const char *url_path = given + u.field_data[UF_PATH].off; - size_t url_path_len = u.field_data[UF_PATH].len; - git_buf_put(&path, url_path, url_path_len); - } else { - git_buf_puts(&path, "/"); - } - - if (has_query) { - const char *url_query = given + u.field_data[UF_QUERY].off; - size_t url_query_len = u.field_data[UF_QUERY].len; - git_buf_decode_percent(&query, url_query, url_query_len); - } - - if (has_userinfo) { - const char *url_userinfo = given + u.field_data[UF_USERINFO].off; - size_t url_userinfo_len = u.field_data[UF_USERINFO].len; - const char *colon = memchr(url_userinfo, ':', url_userinfo_len); - - if (colon) { - const char *url_username = url_userinfo; - size_t url_username_len = colon - url_userinfo; - const char *url_password = colon + 1; - size_t url_password_len = url_userinfo_len - (url_username_len + 1); - - git_buf_decode_percent(&username, url_username, url_username_len); - git_buf_decode_percent(&password, url_password, url_password_len); - } else { - git_buf_decode_percent(&username, url_userinfo, url_userinfo_len); - } - } - - if (git_buf_oom(&scheme) || - git_buf_oom(&host) || - git_buf_oom(&port) || - git_buf_oom(&path) || - git_buf_oom(&query) || - git_buf_oom(&username) || - git_buf_oom(&password)) - return -1; - - url->scheme = git_buf_detach(&scheme); - url->host = git_buf_detach(&host); - url->port = git_buf_detach(&port); - url->path = git_buf_detach(&path); - url->query = git_buf_detach(&query); - url->username = git_buf_detach(&username); - url->password = git_buf_detach(&password); - - error = 0; - -done: - git_buf_dispose(&scheme); - git_buf_dispose(&host); - git_buf_dispose(&port); - git_buf_dispose(&path); - git_buf_dispose(&query); - git_buf_dispose(&username); - git_buf_dispose(&password); - return error; -} - -int git_net_url_joinpath( - git_net_url *out, - git_net_url *one, - const char *two) -{ - git_buf path = GIT_BUF_INIT; - const char *query; - size_t one_len, two_len; - - git_net_url_dispose(out); - - if ((query = strchr(two, '?')) != NULL) { - two_len = query - two; - - if (*(++query) != '\0') { - out->query = git__strdup(query); - GIT_ERROR_CHECK_ALLOC(out->query); - } - } else { - two_len = strlen(two); - } - - /* Strip all trailing `/`s from the first path */ - one_len = one->path ? strlen(one->path) : 0; - while (one_len && one->path[one_len - 1] == '/') - one_len--; - - /* Strip all leading `/`s from the second path */ - while (*two == '/') { - two++; - two_len--; - } - - git_buf_put(&path, one->path, one_len); - git_buf_putc(&path, '/'); - git_buf_put(&path, two, two_len); - - if (git_buf_oom(&path)) - return -1; - - out->path = git_buf_detach(&path); - - if (one->scheme) { - out->scheme = git__strdup(one->scheme); - GIT_ERROR_CHECK_ALLOC(out->scheme); - } - - if (one->host) { - out->host = git__strdup(one->host); - GIT_ERROR_CHECK_ALLOC(out->host); - } - - if (one->port) { - out->port = git__strdup(one->port); - GIT_ERROR_CHECK_ALLOC(out->port); - } - - if (one->username) { - out->username = git__strdup(one->username); - GIT_ERROR_CHECK_ALLOC(out->username); - } - - if (one->password) { - out->password = git__strdup(one->password); - GIT_ERROR_CHECK_ALLOC(out->password); - } - - return 0; -} - -/* - * Some servers strip the query parameters from the Location header - * when sending a redirect. Others leave it in place. - * Check for both, starting with the stripped case first, - * since it appears to be more common. - */ -static void remove_service_suffix( - git_net_url *url, - const char *service_suffix) -{ - const char *service_query = strchr(service_suffix, '?'); - size_t full_suffix_len = strlen(service_suffix); - size_t suffix_len = service_query ? - (size_t)(service_query - service_suffix) : full_suffix_len; - size_t path_len = strlen(url->path); - ssize_t truncate = -1; - - /* - * Check for a redirect without query parameters, - * like "/newloc/info/refs"' - */ - if (suffix_len && path_len >= suffix_len) { - size_t suffix_offset = path_len - suffix_len; - - if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 && - (!service_query || git__strcmp(url->query, service_query + 1) == 0)) { - truncate = suffix_offset; - } - } - - /* - * If we haven't already found where to truncate to remove the - * suffix, check for a redirect with query parameters, like - * "/newloc/info/refs?service=git-upload-pack" - */ - if (truncate < 0 && git__suffixcmp(url->path, service_suffix) == 0) - truncate = path_len - full_suffix_len; - - /* Ensure we leave a minimum of '/' as the path */ - if (truncate == 0) - truncate++; - - if (truncate > 0) { - url->path[truncate] = '\0'; - - git__free(url->query); - url->query = NULL; - } -} - -int git_net_url_apply_redirect( - git_net_url *url, - const char *redirect_location, - const char *service_suffix) -{ - git_net_url tmp = GIT_NET_URL_INIT; - int error = 0; - - GIT_ASSERT(url); - GIT_ASSERT(redirect_location); - - if (redirect_location[0] == '/') { - git__free(url->path); - - if ((url->path = git__strdup(redirect_location)) == NULL) { - error = -1; - goto done; - } - } else { - git_net_url *original = url; - - if ((error = git_net_url_parse(&tmp, redirect_location)) < 0) - goto done; - - /* Validate that this is a legal redirection */ - - if (original->scheme && - strcmp(original->scheme, tmp.scheme) != 0 && - strcmp(tmp.scheme, "https") != 0) { - git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", - original->scheme, tmp.scheme); - - error = -1; - goto done; - } - - if (original->host && - git__strcasecmp(original->host, tmp.host) != 0) { - git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'", - original->host, tmp.host); - - error = -1; - goto done; - } - - git_net_url_swap(url, &tmp); - } - - /* Remove the service suffix if it was given to us */ - if (service_suffix) - remove_service_suffix(url, service_suffix); - -done: - git_net_url_dispose(&tmp); - return error; -} - -bool git_net_url_valid(git_net_url *url) -{ - return (url->host && url->port && url->path); -} - -bool git_net_url_is_default_port(git_net_url *url) -{ - const char *default_port; - - if ((default_port = default_port_for_scheme(url->scheme)) != NULL) - return (strcmp(url->port, default_port) == 0); - else - return false; -} - -bool git_net_url_is_ipv6(git_net_url *url) -{ - return (strchr(url->host, ':') != NULL); -} - -void git_net_url_swap(git_net_url *a, git_net_url *b) -{ - git_net_url tmp = GIT_NET_URL_INIT; - - memcpy(&tmp, a, sizeof(git_net_url)); - memcpy(a, b, sizeof(git_net_url)); - memcpy(b, &tmp, sizeof(git_net_url)); -} - -int git_net_url_fmt(git_buf *buf, git_net_url *url) -{ - GIT_ASSERT_ARG(url); - GIT_ASSERT_ARG(url->scheme); - GIT_ASSERT_ARG(url->host); - - git_buf_puts(buf, url->scheme); - git_buf_puts(buf, "://"); - - if (url->username) { - git_buf_puts(buf, url->username); - - if (url->password) { - git_buf_puts(buf, ":"); - git_buf_puts(buf, url->password); - } - - git_buf_putc(buf, '@'); - } - - git_buf_puts(buf, url->host); - - if (url->port && !git_net_url_is_default_port(url)) { - git_buf_putc(buf, ':'); - git_buf_puts(buf, url->port); - } - - git_buf_puts(buf, url->path ? url->path : "/"); - - if (url->query) { - git_buf_putc(buf, '?'); - git_buf_puts(buf, url->query); - } - - return git_buf_oom(buf) ? -1 : 0; -} - -int git_net_url_fmt_path(git_buf *buf, git_net_url *url) -{ - git_buf_puts(buf, url->path ? url->path : "/"); - - if (url->query) { - git_buf_putc(buf, '?'); - git_buf_puts(buf, url->query); - } - - return git_buf_oom(buf) ? -1 : 0; -} - -static bool matches_pattern( - git_net_url *url, - const char *pattern, - size_t pattern_len) -{ - const char *domain, *port = NULL, *colon; - size_t host_len, domain_len, port_len = 0, wildcard = 0; - - GIT_UNUSED(url); - GIT_UNUSED(pattern); - - if (!pattern_len) - return false; - else if (pattern_len == 1 && pattern[0] == '*') - return true; - else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.') - wildcard = 2; - else if (pattern[0] == '.') - wildcard = 1; - - domain = pattern + wildcard; - domain_len = pattern_len - wildcard; - - if ((colon = memchr(domain, ':', domain_len)) != NULL) { - domain_len = colon - domain; - port = colon + 1; - port_len = pattern_len - wildcard - domain_len - 1; - } - - /* A pattern's port *must* match if it's specified */ - if (port_len && git__strlcmp(url->port, port, port_len) != 0) - return false; - - /* No wildcard? Host must match exactly. */ - if (!wildcard) - return !git__strlcmp(url->host, domain, domain_len); - - /* Wildcard: ensure there's (at least) a suffix match */ - if ((host_len = strlen(url->host)) < domain_len || - memcmp(url->host + (host_len - domain_len), domain, domain_len)) - return false; - - /* The pattern is *.domain and the host is simply domain */ - if (host_len == domain_len) - return true; - - /* The pattern is *.domain and the host is foo.domain */ - return (url->host[host_len - domain_len - 1] == '.'); -} - -bool git_net_url_matches_pattern(git_net_url *url, const char *pattern) -{ - return matches_pattern(url, pattern, strlen(pattern)); -} - -bool git_net_url_matches_pattern_list( - git_net_url *url, - const char *pattern_list) -{ - const char *pattern, *pattern_end, *sep; - - for (pattern = pattern_list; - pattern && *pattern; - pattern = sep ? sep + 1 : NULL) { - sep = strchr(pattern, ','); - pattern_end = sep ? sep : strchr(pattern, '\0'); - - if (matches_pattern(url, pattern, (pattern_end - pattern))) - return true; - } - - return false; -} - -void git_net_url_dispose(git_net_url *url) -{ - if (url->username) - git__memzero(url->username, strlen(url->username)); - - if (url->password) - git__memzero(url->password, strlen(url->password)); - - git__free(url->scheme); url->scheme = NULL; - git__free(url->host); url->host = NULL; - git__free(url->port); url->port = NULL; - git__free(url->path); url->path = NULL; - git__free(url->query); url->query = NULL; - git__free(url->username); url->username = NULL; - git__free(url->password); url->password = NULL; -} |