Commit c6ab183e by Edward Thomson

net: rename gitno_connection_data to git_net_url

"Connection data" is an imprecise and largely incorrect name; these
structures are actually parsed URLs.  Provide a parser that takes a URL
string and produces a URL structure (if it is valid).

Separate the HTTP redirect handling logic from URL parsing, keeping a
`gitno_connection_data_handle_redirect` whose only job is redirect
handling logic and does not parse URLs itself.
parent f4584a1e
/*
* 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 "global.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_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_decode_percent(&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_is_default_port(git_net_url *url)
{
return (strcmp(url->port, default_port_for_scheme(url->scheme)) == 0);
}
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));
}
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->username); url->username = NULL;
git__free(url->password); url->password = NULL;
}
/*
* 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.
*/
#ifndef INCLUDE_net_h__
#define INCLUDE_net_h__
#include "common.h"
typedef struct git_net_url {
char *scheme;
char *host;
char *port;
char *path;
char *query;
char *username;
char *password;
} git_net_url;
#define GIT_NET_URL_INIT { NULL }
/** Parses a string containing a URL into a structure. */
int git_net_url_parse(git_net_url *url, const char *str);
/** Returns nonzero if the URL is on the default port. */
int git_net_url_is_default_port(git_net_url *url);
/** Swaps the contents of one URL for another. */
void git_net_url_swap(git_net_url *a, git_net_url *b);
/** Disposes the contents of the structure. */
void git_net_url_dispose(git_net_url *url);
#endif
...@@ -119,192 +119,79 @@ int gitno__match_host(const char *pattern, const char *host) ...@@ -119,192 +119,79 @@ int gitno__match_host(const char *pattern, const char *host)
return -1; return -1;
} }
static const char *default_port_http = "80"; int gitno_connection_data_handle_redirect(
static const char *default_port_https = "443"; git_net_url *url,
const char *redirect_str,
const char *gitno__default_port(
gitno_connection_data *data)
{
return data->use_ssl ? default_port_https : default_port_http;
}
static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
int gitno_connection_data_from_url(
gitno_connection_data *data,
const char *url,
const char *service_suffix) const char *service_suffix)
{ {
int error = -1; git_net_url tmp = GIT_NET_URL_INIT;
const char *default_port = NULL, *path_search_start = NULL; int error = 0;
char *original_host = NULL;
/* service_suffix is optional */
assert(data && url);
/* Save these for comparison later */ assert(url && redirect_str);
original_host = data->host;
data->host = NULL;
gitno_connection_data_free_ptrs(data);
if (!git__prefixcmp(url, prefix_http)) { if (redirect_str[0] == '/') {
path_search_start = url + strlen(prefix_http); git__free(url->path);
default_port = default_port_http;
if (data->use_ssl) { if ((url->path = git__strdup(redirect_str)) == NULL) {
git_error_set(GIT_ERROR_NET, "redirect from HTTPS to HTTP is not allowed"); error = -1;
goto cleanup; goto done;
}
} else if (!git__prefixcmp(url, prefix_https)) {
path_search_start = url + strlen(prefix_https);
default_port = default_port_https;
data->use_ssl = true;
} else if (url[0] == '/')
default_port = gitno__default_port(data);
if (!default_port) {
git_error_set(GIT_ERROR_NET, "unrecognized URL prefix");
goto cleanup;
} }
} else {
git_net_url *original = url;
error = gitno_extract_url_parts( if ((error = git_net_url_parse(&tmp, redirect_str)) < 0)
&data->host, &data->port, &data->path, &data->user, &data->pass, goto done;
url, default_port);
if (url[0] == '/') { /* Validate that this is a legal redirection */
/* Relative redirect; reuse original host name and port */
path_search_start = url;
git__free(data->host);
data->host = original_host;
original_host = NULL;
}
if (!error) { if (original->scheme &&
const char *path = strchr(path_search_start, '/'); strcmp(original->scheme, tmp.scheme) != 0 &&
size_t pathlen = strlen(path); strcmp(tmp.scheme, "https") != 0) {
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0; git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->scheme, tmp.scheme);
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
git__free(data->path);
data->path = git__strndup(path, pathlen - suffixlen);
} else {
git__free(data->path);
data->path = git__strdup(path);
}
/* Check for errors in the resulting data */
if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
git_error_set(GIT_ERROR_NET, "cross host redirect not allowed");
error = -1; error = -1;
goto done;
} }
}
cleanup:
if (original_host) git__free(original_host);
return error;
}
void gitno_connection_data_free_ptrs(gitno_connection_data *d)
{
git__free(d->host); d->host = NULL;
git__free(d->port); d->port = NULL;
git__free(d->path); d->path = NULL;
git__free(d->user); d->user = NULL;
git__free(d->pass); d->pass = NULL;
}
int gitno_extract_url_parts( if (original->host &&
char **host_out, git__strcasecmp(original->host, tmp.host) != 0) {
char **port_out, git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
char **path_out, original->host, tmp.host);
char **username_out,
char **password_out,
const char *url,
const char *default_port)
{
struct http_parser_url u = {0};
bool has_host, has_port, has_path, has_userinfo;
git_buf host = GIT_BUF_INIT,
port = GIT_BUF_INIT,
path = GIT_BUF_INIT,
username = GIT_BUF_INIT,
password = GIT_BUF_INIT;
int error = 0;
if (http_parser_parse_url(url, strlen(url), false, &u)) { error = -1;
git_error_set(GIT_ERROR_NET, "malformed URL '%s'", url);
error = GIT_EINVALIDSPEC;
goto done; goto done;
} }
has_host = !!(u.field_set & (1 << UF_HOST)); git_net_url_swap(url, &tmp);
has_port = !!(u.field_set & (1 << UF_PORT));
has_path = !!(u.field_set & (1 << UF_PATH));
has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
if (has_host) {
const char *url_host = url + 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) { /* Remove the service suffix if it was given to us */
const char *url_port = url + u.field_data[UF_PORT].off; if (service_suffix) {
size_t url_port_len = u.field_data[UF_PORT].len; const char *service_query = strchr(service_suffix, '?');
git_buf_put(&port, url_port, url_port_len); size_t suffix_len = service_query ?
} else { (size_t)(service_query - service_suffix) : strlen(service_suffix);
git_buf_puts(&port, default_port); size_t path_len = strlen(url->path);
}
if (has_path && path_out) { if (suffix_len && path_len >= suffix_len) {
const char *url_path = url + u.field_data[UF_PATH].off; size_t suffix_offset = path_len - suffix_len;
size_t url_path_len = u.field_data[UF_PATH].len;
git_buf_decode_percent(&path, url_path, url_path_len);
} else if (path_out) {
git_error_set(GIT_ERROR_NET, "invalid url, missing path");
error = GIT_EINVALIDSPEC;
goto done;
}
if (has_userinfo) { if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
const char *url_userinfo = url + u.field_data[UF_USERINFO].off; (!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
size_t url_userinfo_len = u.field_data[UF_USERINFO].len; /* Ensure we leave a minimum of '/' as the path */
const char *colon = memchr(url_userinfo, ':', url_userinfo_len); if (suffix_offset == 0)
suffix_offset++;
if (colon) { url->path[suffix_offset] = '\0';
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__free(url->query);
git_buf_decode_percent(&password, url_password, url_password_len); url->query = NULL;
} else { }
git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
} }
} }
if (git_buf_oom(&host) ||
git_buf_oom(&port) ||
git_buf_oom(&path) ||
git_buf_oom(&username) ||
git_buf_oom(&password))
return -1;
*host_out = git_buf_detach(&host);
*port_out = git_buf_detach(&port);
if (path_out)
*path_out = git_buf_detach(&path);
*username_out = git_buf_detach(&username);
*password_out = git_buf_detach(&password);
done: done:
git_buf_dispose(&host); git_net_url_dispose(&tmp);
git_buf_dispose(&port);
git_buf_dispose(&path);
git_buf_dispose(&username);
git_buf_dispose(&password);
return error; return error;
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "posix.h" #include "posix.h"
#include "stream.h" #include "stream.h"
#include "net.h"
#ifdef GIT_OPENSSL #ifdef GIT_OPENSSL
# include <openssl/ssl.h> # include <openssl/ssl.h>
...@@ -64,38 +65,15 @@ int gitno_recv(gitno_buffer *buf); ...@@ -64,38 +65,15 @@ int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons); void gitno_consume_n(gitno_buffer *buf, size_t cons);
typedef struct gitno_connection_data {
char *host;
char *port;
char *path;
char *user;
char *pass;
bool use_ssl;
} gitno_connection_data;
/* /*
* This replaces all the pointers in `data` with freshly-allocated strings, * This replaces all the pointers in `data` with freshly-allocated strings,
* that the caller is responsible for freeing. * that the caller is responsible for freeing.
* `gitno_connection_data_free_ptrs` is good for this. * `gitno_connection_data_free_ptrs` is good for this.
*/ */
int gitno_connection_data_from_url( int gitno_connection_data_handle_redirect(
gitno_connection_data *data, git_net_url *data,
const char *url, const char *url,
const char *service_suffix); const char *service_suffix);
/* This frees all the pointers IN the struct, but not the struct itself. */
void gitno_connection_data_free_ptrs(gitno_connection_data *data);
int gitno_extract_url_parts(
char **host,
char **port,
char **path,
char **username,
char **password,
const char *url,
const char *default_port);
const char *gitno__default_port(gitno_connection_data *data);
#endif #endif
...@@ -56,18 +56,18 @@ static git_http_auth_context basic_context = { ...@@ -56,18 +56,18 @@ static git_http_auth_context basic_context = {
}; };
int git_http_auth_basic( int git_http_auth_basic(
git_http_auth_context **out, const gitno_connection_data *connection_data) git_http_auth_context **out, const git_net_url *url)
{ {
GIT_UNUSED(connection_data); GIT_UNUSED(url);
*out = &basic_context; *out = &basic_context;
return 0; return 0;
} }
int git_http_auth_dummy( int git_http_auth_dummy(
git_http_auth_context **out, const gitno_connection_data *connection_data) git_http_auth_context **out, const git_net_url *url)
{ {
GIT_UNUSED(connection_data); GIT_UNUSED(url);
*out = NULL; *out = NULL;
return 0; return 0;
......
...@@ -50,15 +50,15 @@ typedef struct { ...@@ -50,15 +50,15 @@ typedef struct {
/** Function to initialize an authentication context */ /** Function to initialize an authentication context */
int (*init_context)( int (*init_context)(
git_http_auth_context **out, git_http_auth_context **out,
const gitno_connection_data *connection_data); const git_net_url *url);
} git_http_auth_scheme; } git_http_auth_scheme;
int git_http_auth_dummy( int git_http_auth_dummy(
git_http_auth_context **out, git_http_auth_context **out,
const gitno_connection_data *connection_data); const git_net_url *url);
int git_http_auth_basic( int git_http_auth_basic(
git_http_auth_context **out, git_http_auth_context **out,
const gitno_connection_data *connection_data); const git_net_url *url);
#endif #endif
...@@ -194,7 +194,7 @@ static void negotiate_context_free(git_http_auth_context *c) ...@@ -194,7 +194,7 @@ static void negotiate_context_free(git_http_auth_context *c)
static int negotiate_init_context( static int negotiate_init_context(
http_auth_negotiate_context *ctx, http_auth_negotiate_context *ctx,
const gitno_connection_data *connection_data) const git_net_url *url)
{ {
OM_uint32 status_major, status_minor; OM_uint32 status_major, status_minor;
gss_OID item, *oid; gss_OID item, *oid;
...@@ -235,7 +235,7 @@ static int negotiate_init_context( ...@@ -235,7 +235,7 @@ static int negotiate_init_context(
} }
git_buf_puts(&ctx->target, "HTTP@"); git_buf_puts(&ctx->target, "HTTP@");
git_buf_puts(&ctx->target, connection_data->host); git_buf_puts(&ctx->target, url->host);
if (git_buf_oom(&ctx->target)) if (git_buf_oom(&ctx->target))
return -1; return -1;
...@@ -248,7 +248,7 @@ static int negotiate_init_context( ...@@ -248,7 +248,7 @@ static int negotiate_init_context(
int git_http_auth_negotiate( int git_http_auth_negotiate(
git_http_auth_context **out, git_http_auth_context **out,
const gitno_connection_data *connection_data) const git_net_url *url)
{ {
http_auth_negotiate_context *ctx; http_auth_negotiate_context *ctx;
...@@ -257,7 +257,7 @@ int git_http_auth_negotiate( ...@@ -257,7 +257,7 @@ int git_http_auth_negotiate(
ctx = git__calloc(1, sizeof(http_auth_negotiate_context)); ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
GIT_ERROR_CHECK_ALLOC(ctx); GIT_ERROR_CHECK_ALLOC(ctx);
if (negotiate_init_context(ctx, connection_data) < 0) { if (negotiate_init_context(ctx, url) < 0) {
git__free(ctx); git__free(ctx);
return -1; return -1;
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
extern int git_http_auth_negotiate( extern int git_http_auth_negotiate(
git_http_auth_context **out, git_http_auth_context **out,
const gitno_connection_data *connection_data); const git_net_url *url);
#else #else
......
...@@ -192,8 +192,9 @@ static int _git_uploadpack_ls( ...@@ -192,8 +192,9 @@ static int _git_uploadpack_ls(
const char *url, const char *url,
git_smart_subtransport_stream **stream) git_smart_subtransport_stream **stream)
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; git_net_url urldata = GIT_NET_URL_INIT;
const char *stream_url = url; const char *stream_url = url;
const char *host, *port;
git_proto_stream *s; git_proto_stream *s;
int error; int error;
...@@ -202,17 +203,15 @@ static int _git_uploadpack_ls( ...@@ -202,17 +203,15 @@ static int _git_uploadpack_ls(
if (!git__prefixcmp(url, prefix_git)) if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(prefix_git); stream_url += strlen(prefix_git);
if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) if ((error = git_net_url_parse(&urldata, url)) < 0)
return error; return error;
error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream); host = urldata.host;
port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
git__free(host); error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
git__free(port);
git__free(path);
git__free(user);
git__free(pass);
git_net_url_dispose(&urldata);
if (error < 0) { if (error < 0) {
git_proto_stream_free(*stream); git_proto_stream_free(*stream);
...@@ -251,7 +250,7 @@ static int _git_receivepack_ls( ...@@ -251,7 +250,7 @@ static int _git_receivepack_ls(
const char *url, const char *url,
git_smart_subtransport_stream **stream) git_smart_subtransport_stream **stream)
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; git_net_url urldata = GIT_NET_URL_INIT;
const char *stream_url = url; const char *stream_url = url;
git_proto_stream *s; git_proto_stream *s;
int error; int error;
...@@ -260,16 +259,12 @@ static int _git_receivepack_ls( ...@@ -260,16 +259,12 @@ static int _git_receivepack_ls(
if (!git__prefixcmp(url, prefix_git)) if (!git__prefixcmp(url, prefix_git))
stream_url += strlen(prefix_git); stream_url += strlen(prefix_git);
if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) if ((error = git_net_url_parse(&urldata, url)) < 0)
return error; return error;
error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream); error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
git__free(host); git_net_url_dispose(&urldata);
git__free(port);
git__free(path);
git__free(user);
git__free(pass);
if (error < 0) { if (error < 0) {
git_proto_stream_free(*stream); git_proto_stream_free(*stream);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "git2.h" #include "git2.h"
#include "http_parser.h" #include "http_parser.h"
#include "buffer.h" #include "buffer.h"
#include "net.h"
#include "netops.h" #include "netops.h"
#include "global.h" #include "global.h"
#include "remote.h" #include "remote.h"
...@@ -71,7 +72,7 @@ typedef struct { ...@@ -71,7 +72,7 @@ typedef struct {
} http_stream; } http_stream;
typedef struct { typedef struct {
gitno_connection_data url; git_net_url url;
git_stream *stream; git_stream *stream;
git_cred *cred; git_cred *cred;
...@@ -191,10 +192,10 @@ static int apply_credentials( ...@@ -191,10 +192,10 @@ static int apply_credentials(
git_http_auth_context *context; git_http_auth_context *context;
/* Apply the credentials given to us in the URL */ /* Apply the credentials given to us in the URL */
if (!cred && server->url.user && server->url.pass) { if (!cred && server->url.username && server->url.password) {
if (!server->url_cred && if (!server->url_cred &&
git_cred_userpass_plaintext_new(&server->url_cred, git_cred_userpass_plaintext_new(&server->url_cred,
server->url.user, server->url.pass) < 0) server->url.username, server->url.password) < 0)
return -1; return -1;
cred = server->url_cred; cred = server->url_cred;
...@@ -226,7 +227,7 @@ static int gen_request( ...@@ -226,7 +227,7 @@ static int gen_request(
if (t->proxy_opts.type == GIT_PROXY_SPECIFIED) if (t->proxy_opts.type == GIT_PROXY_SPECIFIED)
git_buf_printf(buf, "%s %s://%s:%s%s%s HTTP/1.1\r\n", git_buf_printf(buf, "%s %s://%s:%s%s%s HTTP/1.1\r\n",
s->verb, s->verb,
t->server.url.use_ssl ? "https" : "http", t->server.url.scheme,
t->server.url.host, t->server.url.host,
t->server.url.port, t->server.url.port,
path, s->service_url); path, s->service_url);
...@@ -238,9 +239,10 @@ static int gen_request( ...@@ -238,9 +239,10 @@ static int gen_request(
git_http__user_agent(buf); git_http__user_agent(buf);
git_buf_puts(buf, "\r\n"); git_buf_puts(buf, "\r\n");
git_buf_printf(buf, "Host: %s", t->server.url.host); git_buf_printf(buf, "Host: %s", t->server.url.host);
if (strcmp(t->server.url.port, gitno__default_port(&t->server.url)) != 0) {
if (!git_net_url_is_default_port(&t->server.url))
git_buf_printf(buf, ":%s", t->server.url.port); git_buf_printf(buf, ":%s", t->server.url.port);
}
git_buf_puts(buf, "\r\n"); git_buf_puts(buf, "\r\n");
if (s->chunked || content_length > 0) { if (s->chunked || content_length > 0) {
...@@ -484,7 +486,7 @@ static int on_headers_complete(http_parser *parser) ...@@ -484,7 +486,7 @@ static int on_headers_complete(http_parser *parser)
SERVER_TYPE_PROXY, SERVER_TYPE_PROXY,
t->proxy_opts.credentials, t->proxy_opts.credentials,
t->proxy_opts.payload, t->proxy_opts.payload,
t->proxy.url.user, t->proxy.url.username,
proxy_auth_types); proxy_auth_types);
/* Check for an authentication failure. */ /* Check for an authentication failure. */
...@@ -495,7 +497,7 @@ static int on_headers_complete(http_parser *parser) ...@@ -495,7 +497,7 @@ static int on_headers_complete(http_parser *parser)
SERVER_TYPE_REMOTE, SERVER_TYPE_REMOTE,
t->owner->cred_acquire_cb, t->owner->cred_acquire_cb,
t->owner->cred_acquire_payload, t->owner->cred_acquire_payload,
t->server.url.user, t->server.url.username,
server_auth_types); server_auth_types);
/* Check for a redirect. /* Check for a redirect.
...@@ -507,7 +509,7 @@ static int on_headers_complete(http_parser *parser) ...@@ -507,7 +509,7 @@ static int on_headers_complete(http_parser *parser)
parser->status_code == 308) && parser->status_code == 308) &&
t->location) { t->location) {
if (gitno_connection_data_from_url(&t->server.url, t->location, s->service_url) < 0) if (gitno_connection_data_handle_redirect(&t->server.url, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
/* Set the redirect URL on the stream. This is a transfer of /* Set the redirect URL on the stream. This is a transfer of
...@@ -676,7 +678,7 @@ static int load_proxy_config(http_subtransport *t) ...@@ -676,7 +678,7 @@ static int load_proxy_config(http_subtransport *t)
git_proxy_init_options(&t->proxy_opts, GIT_PROXY_OPTIONS_VERSION); git_proxy_init_options(&t->proxy_opts, GIT_PROXY_OPTIONS_VERSION);
if ((error = git_remote__get_http_proxy(t->owner->owner, if ((error = git_remote__get_http_proxy(t->owner->owner,
!!t->server.url.use_ssl, &t->proxy_url)) < 0) !strcmp(t->server.url.scheme, "https"), &t->proxy_url)) < 0)
return error; return error;
if (!t->proxy_url) if (!t->proxy_url)
...@@ -698,10 +700,11 @@ static int load_proxy_config(http_subtransport *t) ...@@ -698,10 +700,11 @@ static int load_proxy_config(http_subtransport *t)
return -1; return -1;
} }
if ((error = gitno_connection_data_from_url(&t->proxy.url, t->proxy_opts.url, NULL)) < 0) git_net_url_dispose(&t->proxy.url);
if ((error = git_net_url_parse(&t->proxy.url, t->proxy_opts.url)) < 0)
return error; return error;
if (t->proxy.url.use_ssl) { if (!strcmp(t->proxy.url.scheme, "https")) {
git_error_set(GIT_ERROR_NET, "SSL connections to proxy are not supported"); git_error_set(GIT_ERROR_NET, "SSL connections to proxy are not supported");
return -1; return -1;
} }
...@@ -711,7 +714,7 @@ static int load_proxy_config(http_subtransport *t) ...@@ -711,7 +714,7 @@ static int load_proxy_config(http_subtransport *t)
static int check_certificate( static int check_certificate(
git_stream *stream, git_stream *stream,
gitno_connection_data *url, git_net_url *url,
int is_valid, int is_valid,
git_transport_certificate_check_cb cert_cb, git_transport_certificate_check_cb cert_cb,
void *cert_cb_payload) void *cert_cb_payload)
...@@ -740,7 +743,7 @@ static int check_certificate( ...@@ -740,7 +743,7 @@ static int check_certificate(
static int stream_connect( static int stream_connect(
git_stream *stream, git_stream *stream,
gitno_connection_data *url, git_net_url *url,
git_transport_certificate_check_cb cert_cb, git_transport_certificate_check_cb cert_cb,
void *cb_payload) void *cb_payload)
{ {
...@@ -812,7 +815,7 @@ static int proxy_headers_complete(http_parser *parser) ...@@ -812,7 +815,7 @@ static int proxy_headers_complete(http_parser *parser)
SERVER_TYPE_PROXY, SERVER_TYPE_PROXY,
t->proxy_opts.credentials, t->proxy_opts.credentials,
t->proxy_opts.payload, t->proxy_opts.payload,
t->proxy.url.user, t->proxy.url.username,
proxy_auth_types); proxy_auth_types);
if (parser->status_code != 200) { if (parser->status_code != 200) {
...@@ -927,7 +930,7 @@ done: ...@@ -927,7 +930,7 @@ done:
static int http_connect(http_subtransport *t) static int http_connect(http_subtransport *t)
{ {
gitno_connection_data *url; git_net_url *url;
git_stream *proxy_stream = NULL, *stream = NULL; git_stream *proxy_stream = NULL, *stream = NULL;
git_transport_certificate_check_cb cert_cb; git_transport_certificate_check_cb cert_cb;
void *cb_payload; void *cb_payload;
...@@ -965,7 +968,7 @@ static int http_connect(http_subtransport *t) ...@@ -965,7 +968,7 @@ static int http_connect(http_subtransport *t)
cb_payload = t->owner->message_cb_payload; cb_payload = t->owner->message_cb_payload;
} }
if (url->use_ssl) if (strcmp(url->scheme, "https") == 0)
error = git_tls_stream_new(&stream, url->host, url->port); error = git_tls_stream_new(&stream, url->host, url->port);
else else
error = git_socket_stream_new(&stream, url->host, url->port); error = git_socket_stream_new(&stream, url->host, url->port);
...@@ -982,7 +985,7 @@ static int http_connect(http_subtransport *t) ...@@ -982,7 +985,7 @@ static int http_connect(http_subtransport *t)
* an HTTPS connection, then we need to build a CONNECT tunnel. * an HTTPS connection, then we need to build a CONNECT tunnel.
*/ */
if (t->proxy_opts.type == GIT_PROXY_SPECIFIED && if (t->proxy_opts.type == GIT_PROXY_SPECIFIED &&
t->server.url.use_ssl) { strcmp(t->server.url.scheme, "https") == 0) {
proxy_stream = stream; proxy_stream = stream;
stream = NULL; stream = NULL;
...@@ -1378,7 +1381,7 @@ static int http_action( ...@@ -1378,7 +1381,7 @@ static int http_action(
* that would be insecure in plaintext (eg, HTTP Basic). * that would be insecure in plaintext (eg, HTTP Basic).
*/ */
if ((!t->server.url.host || !t->server.url.port || !t->server.url.path) && if ((!t->server.url.host || !t->server.url.port || !t->server.url.path) &&
(ret = gitno_connection_data_from_url(&t->server.url, url, NULL)) < 0) (ret = git_net_url_parse(&t->server.url, url)) < 0)
return ret; return ret;
assert(t->server.url.host && t->server.url.port && t->server.url.path); assert(t->server.url.host && t->server.url.port && t->server.url.path);
...@@ -1445,11 +1448,8 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -1445,11 +1448,8 @@ static int http_close(git_smart_subtransport *subtransport)
free_auth_contexts(&t->server.auth_contexts); free_auth_contexts(&t->server.auth_contexts);
free_auth_contexts(&t->proxy.auth_contexts); free_auth_contexts(&t->proxy.auth_contexts);
gitno_connection_data_free_ptrs(&t->server.url); git_net_url_dispose(&t->server.url);
memset(&t->server.url, 0x0, sizeof(gitno_connection_data)); git_net_url_dispose(&t->proxy.url);
gitno_connection_data_free_ptrs(&t->proxy.url);
memset(&t->proxy.url, 0x0, sizeof(gitno_connection_data));
git__free(t->proxy_url); git__free(t->proxy_url);
t->proxy_url = NULL; t->proxy_url = NULL;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "global.h" #include "global.h"
#include "git2.h" #include "git2.h"
#include "buffer.h" #include "buffer.h"
#include "net.h"
#include "netops.h" #include "netops.h"
#include "smart.h" #include "smart.h"
#include "cred.h" #include "cred.h"
...@@ -258,8 +259,7 @@ static int ssh_stream_alloc( ...@@ -258,8 +259,7 @@ static int ssh_stream_alloc(
} }
static int git_ssh_extract_url_parts( static int git_ssh_extract_url_parts(
char **host, git_net_url *urldata,
char **username,
const char *url) const char *url)
{ {
char *colon, *at; char *colon, *at;
...@@ -271,11 +271,11 @@ static int git_ssh_extract_url_parts( ...@@ -271,11 +271,11 @@ static int git_ssh_extract_url_parts(
at = strchr(url, '@'); at = strchr(url, '@');
if (at) { if (at) {
start = at + 1; start = at + 1;
*username = git__substrdup(url, at - url); urldata->username = git__substrdup(url, at - url);
GIT_ERROR_CHECK_ALLOC(*username); GIT_ERROR_CHECK_ALLOC(urldata->username);
} else { } else {
start = url; start = url;
*username = NULL; urldata->username = NULL;
} }
if (colon == NULL || (colon < start)) { if (colon == NULL || (colon < start)) {
...@@ -283,8 +283,8 @@ static int git_ssh_extract_url_parts( ...@@ -283,8 +283,8 @@ static int git_ssh_extract_url_parts(
return -1; return -1;
} }
*host = git__substrdup(start, colon - start); urldata->host = git__substrdup(start, colon - start);
GIT_ERROR_CHECK_ALLOC(*host); GIT_ERROR_CHECK_ALLOC(urldata->host);
return 0; return 0;
} }
...@@ -506,14 +506,15 @@ static int _git_ssh_session_create( ...@@ -506,14 +506,15 @@ static int _git_ssh_session_create(
return 0; return 0;
} }
#define SSH_DEFAULT_PORT "22"
static int _git_ssh_setup_conn( static int _git_ssh_setup_conn(
ssh_subtransport *t, ssh_subtransport *t,
const char *url, const char *url,
const char *cmd, const char *cmd,
git_smart_subtransport_stream **stream) git_smart_subtransport_stream **stream)
{ {
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; git_net_url urldata = GIT_NET_URL_INIT;
const char *default_port="22";
int auth_methods, error = 0; int auth_methods, error = 0;
size_t i; size_t i;
ssh_stream *s; ssh_stream *s;
...@@ -535,19 +536,22 @@ static int _git_ssh_setup_conn( ...@@ -535,19 +536,22 @@ static int _git_ssh_setup_conn(
const char *p = ssh_prefixes[i]; const char *p = ssh_prefixes[i];
if (!git__prefixcmp(url, p)) { if (!git__prefixcmp(url, p)) {
if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0) if ((error = git_net_url_parse(&urldata, url)) < 0)
goto done; goto done;
goto post_extract; goto post_extract;
} }
} }
if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0) if ((error = git_ssh_extract_url_parts(&urldata, url)) < 0)
goto done; goto done;
port = git__strdup(default_port);
GIT_ERROR_CHECK_ALLOC(port); if (urldata.port == NULL)
urldata.port = git__strdup(SSH_DEFAULT_PORT);
GIT_ERROR_CHECK_ALLOC(urldata.port);
post_extract: post_extract:
if ((error = git_socket_stream_new(&s->io, host, port)) < 0 || if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 ||
(error = git_stream_connect(s->io)) < 0) (error = git_stream_connect(s->io)) < 0)
goto done; goto done;
...@@ -583,7 +587,7 @@ post_extract: ...@@ -583,7 +587,7 @@ post_extract:
cert_ptr = &cert; cert_ptr = &cert;
error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, host, t->owner->message_cb_payload); error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
if (error < 0 && error != GIT_PASSTHROUGH) { if (error < 0 && error != GIT_PASSTHROUGH) {
if (!git_error_last()) if (!git_error_last())
...@@ -594,21 +598,21 @@ post_extract: ...@@ -594,21 +598,21 @@ post_extract:
} }
/* we need the username to ask for auth methods */ /* we need the username to ask for auth methods */
if (!user) { if (!urldata.username) {
if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0) if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0)
goto done; goto done;
user = git__strdup(((git_cred_username *) cred)->username); urldata.username = git__strdup(((git_cred_username *) cred)->username);
cred->free(cred); cred->free(cred);
cred = NULL; cred = NULL;
if (!user) if (!urldata.username)
goto done; goto done;
} else if (user && pass) { } else if (urldata.username && urldata.password) {
if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0) if ((error = git_cred_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0)
goto done; goto done;
} }
if ((error = list_auth_methods(&auth_methods, session, user)) < 0) if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
goto done; goto done;
error = GIT_EAUTH; error = GIT_EAUTH;
...@@ -622,10 +626,10 @@ post_extract: ...@@ -622,10 +626,10 @@ post_extract:
cred = NULL; cred = NULL;
} }
if ((error = request_creds(&cred, t, user, auth_methods)) < 0) if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0)
goto done; goto done;
if (strcmp(user, git_cred__username(cred))) { if (strcmp(urldata.username, git_cred__username(cred))) {
git_error_set(GIT_ERROR_SSH, "username does not match previous request"); git_error_set(GIT_ERROR_SSH, "username does not match previous request");
error = -1; error = -1;
goto done; goto done;
...@@ -662,11 +666,7 @@ done: ...@@ -662,11 +666,7 @@ done:
if (cred) if (cred)
cred->free(cred); cred->free(cred);
git__free(host); git_net_url_dispose(&urldata);
git__free(port);
git__free(path);
git__free(user);
git__free(pass);
return error; return error;
} }
......
...@@ -103,8 +103,8 @@ typedef struct { ...@@ -103,8 +103,8 @@ typedef struct {
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_connection_data connection_data; git_net_url url;
gitno_connection_data proxy_connection_data; git_net_url proxy_url;
git_cred *cred; git_cred *cred;
git_cred *url_cred; git_cred *url_cred;
git_cred *proxy_cred; git_cred *proxy_cred;
...@@ -280,7 +280,7 @@ static int certificate_check(winhttp_stream *s, int valid) ...@@ -280,7 +280,7 @@ static int certificate_check(winhttp_stream *s, int valid)
return GIT_ECERTIFICATE; return GIT_ECERTIFICATE;
} }
if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl) if (t->owner->certificate_check_cb == NULL || git__strcmp(t->url.scheme, "https") != 0)
return 0; return 0;
if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) { if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
...@@ -292,7 +292,7 @@ static int certificate_check(winhttp_stream *s, int valid) ...@@ -292,7 +292,7 @@ static int certificate_check(winhttp_stream *s, int valid)
cert.parent.cert_type = GIT_CERT_X509; cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded; cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded; cert.len = cert_ctx->cbCertEncoded;
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->message_cb_payload); error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->url.host, t->owner->message_cb_payload);
CertFreeCertificateContext(cert_ctx); CertFreeCertificateContext(cert_ctx);
if (error == GIT_PASSTHROUGH) if (error == GIT_PASSTHROUGH)
...@@ -329,9 +329,6 @@ static void winhttp_stream_close(winhttp_stream *s) ...@@ -329,9 +329,6 @@ static void winhttp_stream_close(winhttp_stream *s)
s->sent_request = 0; s->sent_request = 0;
} }
#define SCHEME_HTTP "http://"
#define SCHEME_HTTPS "https://"
static int winhttp_stream_connect(winhttp_stream *s) static int winhttp_stream_connect(winhttp_stream *s)
{ {
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
...@@ -348,7 +345,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -348,7 +345,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
const git_proxy_options *proxy_opts; const git_proxy_options *proxy_opts;
/* Prepare URL */ /* Prepare URL */
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); git_buf_printf(&buf, "%s%s", t->url.path, s->service_url);
if (git_buf_oom(&buf)) if (git_buf_oom(&buf))
return -1; return -1;
...@@ -367,7 +364,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -367,7 +364,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL, NULL,
WINHTTP_NO_REFERER, WINHTTP_NO_REFERER,
types, types,
t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0); git__strcmp(t->url.scheme, "https") == 0 ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) { if (!s->request) {
git_error_set(GIT_ERROR_OS, "failed to open request"); git_error_set(GIT_ERROR_OS, "failed to open request");
...@@ -382,7 +379,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -382,7 +379,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
proxy_opts = &t->owner->proxy; proxy_opts = &t->owner->proxy;
if (proxy_opts->type == GIT_PROXY_AUTO) { if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */ /* Set proxy if necessary */
if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0) if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->url.scheme, "https") == 0), &proxy_url) < 0)
goto on_error; goto on_error;
} }
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) { else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
...@@ -395,38 +392,33 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -395,38 +392,33 @@ static int winhttp_stream_connect(winhttp_stream *s)
WINHTTP_PROXY_INFO proxy_info; WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide; wchar_t *proxy_wide;
if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) { git_net_url_dispose(&t->proxy_url);
t->proxy_connection_data.use_ssl = false;
} else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) {
t->proxy_connection_data.use_ssl = true;
} else {
git_error_set(GIT_ERROR_NET, "invalid URL: '%s'", proxy_url);
return -1;
}
gitno_connection_data_free_ptrs(&t->proxy_connection_data); if ((error = git_net_url_parse(&t->proxy_url, proxy_url)) < 0)
goto on_error;
if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL, if (strcmp(t->proxy_url.scheme, "http") != 0 && strcmp(t->proxy_url.scheme, "https") != 0) {
&t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0) git_error_set(GIT_ERROR_NET, "invalid URL: '%s'", proxy_url);
error = -1;
goto on_error; goto on_error;
}
if (t->proxy_connection_data.user && t->proxy_connection_data.pass) { if (t->proxy_url.username && t->proxy_url.password) {
if (t->proxy_cred) { if (t->proxy_cred) {
t->proxy_cred->free(t->proxy_cred); t->proxy_cred->free(t->proxy_cred);
} }
if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0) if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_url.username, t->proxy_url.password)) < 0)
goto on_error; goto on_error;
} }
if (t->proxy_connection_data.use_ssl) git_buf_puts(&processed_url, t->proxy_url.scheme);
git_buf_PUTS(&processed_url, SCHEME_HTTPS); git_buf_PUTS(&processed_url, "://");
else
git_buf_PUTS(&processed_url, SCHEME_HTTP); git_buf_puts(&processed_url, t->proxy_url.host);
git_buf_puts(&processed_url, t->proxy_connection_data.host); if (!git_net_url_is_default_port(&t->proxy_url))
if (t->proxy_connection_data.port) git_buf_printf(&processed_url, ":%s", t->proxy_url.port);
git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
if (git_buf_oom(&processed_url)) { if (git_buf_oom(&processed_url)) {
error = -1; error = -1;
...@@ -543,7 +535,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -543,7 +535,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
} }
/* If requested, disable certificate validation */ /* If requested, disable certificate validation */
if (t->connection_data.use_ssl) { if (strcmp(t->url.scheme, "https") == 0) {
int flags; int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0) if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
...@@ -562,9 +554,9 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -562,9 +554,9 @@ static int winhttp_stream_connect(winhttp_stream *s)
/* If no other credentials have been applied and the URL has username and /* If no other credentials have been applied and the URL has username and
* password, use those */ * password, use those */
if (!t->cred && t->connection_data.user && t->connection_data.pass) { if (!t->cred && t->url.username && t->url.password) {
if (!t->url_cred && if (!t->url_cred &&
git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0) git_cred_userpass_plaintext_new(&t->url_cred, t->url.username, t->url.password) < 0)
goto on_error; goto on_error;
if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0) if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0)
goto on_error; goto on_error;
...@@ -741,12 +733,12 @@ static int winhttp_connect( ...@@ -741,12 +733,12 @@ static int winhttp_connect(
t->connection = NULL; t->connection = NULL;
/* Prepare port */ /* Prepare port */
if (git__strntol32(&port, t->connection_data.port, if (git__strntol32(&port, t->url.port,
strlen(t->connection_data.port), NULL, 10) < 0) strlen(t->url.port), NULL, 10) < 0)
return -1; return -1;
/* Prepare host */ /* Prepare host */
if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) { if (git__utf8_to_16_alloc(&wide_host, t->url.host) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters"); git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
return -1; return -1;
} }
...@@ -1062,7 +1054,7 @@ replay: ...@@ -1062,7 +1054,7 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) { if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */ /* Upgrade to secure connection; disconnect and start over */
if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0) { if (gitno_connection_data_handle_redirect(&t->url, location8, s->service_url) < 0) {
git__free(location8); git__free(location8);
return -1; return -1;
} }
...@@ -1112,7 +1104,7 @@ replay: ...@@ -1112,7 +1104,7 @@ replay:
/* Start with the user-supplied credential callback, if present */ /* Start with the user-supplied credential callback, if present */
if (t->owner->cred_acquire_cb) { if (t->owner->cred_acquire_cb) {
cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url,
t->connection_data.user, allowed_types, t->owner->cred_acquire_payload); t->url.username, allowed_types, t->owner->cred_acquire_payload);
/* Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set */ /* Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set */
if (cred_error == GIT_PASSTHROUGH) if (cred_error == GIT_PASSTHROUGH)
...@@ -1124,7 +1116,7 @@ replay: ...@@ -1124,7 +1116,7 @@ replay:
/* Invoke the fallback credentials acquisition callback if necessary */ /* Invoke the fallback credentials acquisition callback if necessary */
if (cred_error > 0) { if (cred_error > 0) {
cred_error = fallback_cred_acquire_cb(&t->cred, t->owner->url, cred_error = fallback_cred_acquire_cb(&t->cred, t->owner->url,
t->connection_data.user, allowed_types, NULL); t->url.username, allowed_types, NULL);
if (cred_error < 0) if (cred_error < 0)
return cred_error; return cred_error;
...@@ -1496,7 +1488,7 @@ static int winhttp_action( ...@@ -1496,7 +1488,7 @@ static int winhttp_action(
int ret = -1; int ret = -1;
if (!t->connection) if (!t->connection)
if ((ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0 || if ((ret = git_net_url_parse(&t->url, url)) < 0 ||
(ret = winhttp_connect(t)) < 0) (ret = winhttp_connect(t)) < 0)
return ret; return ret;
...@@ -1538,10 +1530,8 @@ static int winhttp_close(git_smart_subtransport *subtransport) ...@@ -1538,10 +1530,8 @@ static int winhttp_close(git_smart_subtransport *subtransport)
{ {
winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
gitno_connection_data_free_ptrs(&t->connection_data); git_net_url_dispose(&t->url);
memset(&t->connection_data, 0x0, sizeof(gitno_connection_data)); git_net_url_dispose(&t->proxy_url);
gitno_connection_data_free_ptrs(&t->proxy_connection_data);
memset(&t->proxy_connection_data, 0x0, sizeof(gitno_connection_data));
if (t->cred) { if (t->cred) {
t->cred->free(t->cred); t->cred->free(t->cred);
......
#include "clar_libgit2.h"
#include "net.h"
#include "netops.h"
static git_net_url conndata;
void test_network_redirect__initialize(void)
{
memset(&conndata, 0, sizeof(conndata));
}
void test_network_redirect__cleanup(void)
{
git_net_url_dispose(&conndata);
}
void test_network_redirect__redirect_http(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"http://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"http://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_redirect__redirect_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"https://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_redirect__redirect_leaves_root_path(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"https://example.com/foo/bar/baz", "/foo/bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_redirect__redirect_encoded_username_password(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_s(conndata.username, "user/name");
cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
}
void test_network_redirect__redirect_cross_host_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
"https://foo.com/bar/baz", NULL),
-1);
}
void test_network_redirect__redirect_http_downgrade_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
"http://foo.com/bar/baz", NULL),
-1);
}
void test_network_redirect__redirect_relative(void)
{
cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_redirect__redirect_relative_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "netops.h" #include "net.h"
static char *host, *port, *path, *user, *pass; static git_net_url conndata;
static gitno_connection_data conndata;
void test_network_urlparse__initialize(void) void test_network_urlparse__initialize(void)
{ {
host = port = path = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata)); memset(&conndata, 0, sizeof(conndata));
} }
void test_network_urlparse__cleanup(void) void test_network_urlparse__cleanup(void)
{ {
#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; } git_net_url_dispose(&conndata);
FREE_AND_NULL(host);
FREE_AND_NULL(port);
FREE_AND_NULL(path);
FREE_AND_NULL(user);
FREE_AND_NULL(pass);
gitno_connection_data_free_ptrs(&conndata);
} }
void test_network_urlparse__trivial(void) void test_network_urlparse__trivial(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource"));
"http://example.com/resource", "8080")); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_p(user, NULL); cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
} }
void test_network_urlparse__root(void) void test_network_urlparse__root(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata, "http://example.com/"));
"http://example.com/", "8080")); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(path, "/"); cl_assert_equal_s(conndata.path, "/");
cl_assert_equal_p(user, NULL); cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
} }
void test_network_urlparse__just_hostname(void) void test_network_urlparse__implied_root(void)
{ {
cl_git_fail_with(GIT_EINVALIDSPEC, cl_git_pass(git_net_url_parse(&conndata, "http://example.com"));
gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_assert_equal_s(conndata.scheme, "http");
"http://example.com", "8080")); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
}
void test_network_urlparse__implied_root_custom_port(void)
{
cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42"));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "42");
cl_assert_equal_s(conndata.path, "/");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
} }
void test_network_urlparse__encoded_password(void) void test_network_urlparse__encoded_password(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://user:pass%2fis%40bad@hostname.com:1234/", "1")); "https://user:pass%2fis%40bad@hostname.com:1234/"));
cl_assert_equal_s(host, "hostname.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "1234"); cl_assert_equal_s(conndata.host, "hostname.com");
cl_assert_equal_s(path, "/"); cl_assert_equal_s(conndata.port, "1234");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(conndata.path, "/");
cl_assert_equal_s(pass, "pass/is@bad"); cl_assert_equal_s(conndata.username, "user");
cl_assert_equal_s(conndata.password, "pass/is@bad");
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
} }
void test_network_urlparse__user(void) void test_network_urlparse__user(void)
{ {
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://user@example.com/resource", "8080")); "https://user@example.com/resource"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_p(pass, NULL); cl_assert_equal_s(conndata.username, "user");
cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
} }
void test_network_urlparse__user_pass(void) void test_network_urlparse__user_pass(void)
{ {
/* user:pass@hostname.tld/resource */ /* user:pass@hostname.tld/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://user:pass@example.com/resource", "8080")); "https://user:pass@example.com/resource"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "8080"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_s(pass, "pass"); cl_assert_equal_s(conndata.username, "user");
cl_assert_equal_s(conndata.password, "pass");
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
} }
void test_network_urlparse__port(void) void test_network_urlparse__port(void)
{ {
/* hostname.tld:port/resource */ /* hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://example.com:9191/resource", "8080")); "https://example.com:9191/resource"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "9191"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(conndata.port, "9191");
cl_assert_equal_p(user, NULL); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_p(pass, NULL); cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
} }
void test_network_urlparse__user_port(void) void test_network_urlparse__user_port(void)
{ {
/* user@hostname.tld:port/resource */ /* user@hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://user@example.com:9191/resource", "8080")); "https://user@example.com:9191/resource"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "9191"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(conndata.port, "9191");
cl_assert_equal_s(user, "user"); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_p(pass, NULL); cl_assert_equal_s(conndata.username, "user");
cl_assert_equal_p(conndata.password, NULL);
cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
} }
void test_network_urlparse__user_pass_port(void) void test_network_urlparse__user_pass_port(void)
{ {
/* user:pass@hostname.tld:port/resource */ /* user:pass@hostname.tld:port/resource */
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, cl_git_pass(git_net_url_parse(&conndata,
"https://user:pass@example.com:9191/resource", "8080")); "https://user:pass@example.com:9191/resource"));
cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(port, "9191");
cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass");
}
void test_network_urlparse__optional_path(void)
{
cl_git_fail(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"https://user:pass@example.com:9191", "8080"));
cl_git_pass(gitno_extract_url_parts(&host, &port, NULL, &user, &pass,
"https://user:pass@example.com:9191", "8080"));
}
void test_network_urlparse__connection_data_http(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, false);
}
void test_network_urlparse__connection_data_ssl(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443"); cl_assert_equal_s(conndata.port, "9191");
cl_assert_equal_s(conndata.path, "/foo/"); cl_assert_equal_s(conndata.path, "/resource");
cl_assert_equal_p(conndata.user, NULL); cl_assert_equal_s(conndata.username, "user");
cl_assert_equal_p(conndata.pass, NULL); cl_assert_equal_s(conndata.password, "pass");
cl_assert_equal_i(conndata.use_ssl, true); cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
}
void test_network_urlparse__encoded_username_password(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_s(conndata.user, "user/name");
cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
cl_assert_equal_i(conndata.use_ssl, true);
}
void test_network_urlparse__connection_data_cross_host_redirect(void)
{
conndata.host = git__strdup("bar.com");
cl_git_fail_with(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz", NULL),
-1);
}
void test_network_urlparse__connection_data_http_downgrade(void)
{
conndata.use_ssl = true;
cl_git_fail_with(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz", NULL),
-1);
}
void test_network_urlparse__connection_data_relative_redirect(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz/biff", NULL));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, false);
}
void test_network_urlparse__connection_data_relative_redirect_ssl(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz/biff", NULL));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, true);
}
/* Run this under valgrind */
void test_network_urlparse__connection_data_cleanup(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz/biff", "baz/biff"));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz/biff", "baz/biff"));
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment