Commit 27307ed6c0071dbf6b670e657dba41564b692ad2

Edward Thomson 2022-01-11T10:39:57

ssh: use url parsing functionality Instead of trying to figure out a repo's path from a URL by hand, parse a URL using the parsing functionality.

diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 0f4a0fc..75b8e8b 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -24,8 +24,6 @@
 
 #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
 
-static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" };
-
 static const char cmd_uploadpack[] = "git-upload-pack";
 static const char cmd_receivepack[] = "git-receive-pack";
 
@@ -35,7 +33,7 @@ typedef struct {
 	LIBSSH2_SESSION *session;
 	LIBSSH2_CHANNEL *channel;
 	const char *cmd;
-	char *url;
+	git_net_url url;
 	unsigned sent_command : 1;
 } ssh_stream;
 
@@ -63,39 +61,23 @@ static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
  *
  * For example: git-upload-pack '/libgit2/libgit2'
  */
-static int gen_proto(git_str *request, const char *cmd, const char *url)
+static int gen_proto(git_str *request, const char *cmd, git_net_url *url)
 {
 	const char *repo;
-	int len;
-	size_t i;
-
-	for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
-		const char *p = ssh_prefixes[i];
 
-		if (!git__prefixcmp(url, p)) {
-			url = url + strlen(p);
-			repo = strchr(url, '/');
-			if (repo && repo[1] == '~')
-				++repo;
+	repo = url->path;
 
-			goto done;
-		}
-	}
-	repo = strchr(url, ':');
-	if (repo) repo++;
+	if (repo && repo[0] == '/' && repo[1] == '~')
+		repo++;
 
-done:
-	if (!repo) {
+	if (!repo || !repo[0]) {
 		git_error_set(GIT_ERROR_NET, "malformed git protocol URL");
 		return -1;
 	}
 
-	len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
-
-	git_str_grow(request, len);
 	git_str_puts(request, cmd);
 	git_str_puts(request, " '");
-	git_str_decode_percent(request, repo, strlen(repo));
+	git_str_puts(request, repo);
 	git_str_puts(request, "'");
 
 	if (git_str_oom(request))
@@ -109,7 +91,7 @@ static int send_command(ssh_stream *s)
 	int error;
 	git_str request = GIT_STR_INIT;
 
-	error = gen_proto(&request, s->cmd, s->url);
+	error = gen_proto(&request, s->cmd, &s->url);
 	if (error < 0)
 		goto cleanup;
 
@@ -224,13 +206,12 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream)
 		s->io = NULL;
 	}
 
-	git__free(s->url);
+	git_net_url_dispose(&s->url);
 	git__free(s);
 }
 
 static int ssh_stream_alloc(
 	ssh_subtransport *t,
-	const char *url,
 	const char *cmd,
 	git_smart_subtransport_stream **stream)
 {
@@ -248,12 +229,6 @@ static int ssh_stream_alloc(
 
 	s->cmd = cmd;
 
-	s->url = git__strdup(url);
-	if (!s->url) {
-		git__free(s);
-		return -1;
-	}
-
 	*stream = &s->parent;
 	return 0;
 }
@@ -487,9 +462,7 @@ static int _git_ssh_setup_conn(
 	const char *cmd,
 	git_smart_subtransport_stream **stream)
 {
-	git_net_url urldata = GIT_NET_URL_INIT;
 	int auth_methods, error = 0;
-	size_t i;
 	ssh_stream *s;
 	git_credential *cred = NULL;
 	LIBSSH2_SESSION *session=NULL;
@@ -498,28 +471,22 @@ static int _git_ssh_setup_conn(
 	t->current_stream = NULL;
 
 	*stream = NULL;
-	if (ssh_stream_alloc(t, url, cmd, stream) < 0)
+	if (ssh_stream_alloc(t, cmd, stream) < 0)
 		return -1;
 
 	s = (ssh_stream *)*stream;
 	s->session = NULL;
 	s->channel = NULL;
 
-	for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
-		const char *p = ssh_prefixes[i];
-
-		if (!git__prefixcmp(url, p)) {
-			if ((error = git_net_url_parse(&urldata, url)) < 0)
-				goto done;
+	if (git_net_str_is_url(url))
+		error = git_net_url_parse(&s->url, url);
+	else
+		error = git_net_url_parse_scp(&s->url, url);
 
-			goto post_extract;
-		}
-	}
-	if ((error = git_net_url_parse_scp(&urldata, url)) < 0)
+	if (error < 0)
 		goto done;
 
-post_extract:
-	if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 ||
+	if ((error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
 	    (error = git_stream_connect(s->io)) < 0)
 		goto done;
 
@@ -603,7 +570,7 @@ post_extract:
 		error = t->owner->connect_opts.callbacks.certificate_check(
 			(git_cert *)cert_ptr,
 			0,
-			urldata.host,
+			s->url.host,
 			t->owner->connect_opts.callbacks.payload);
 
 		if (error < 0 && error != GIT_PASSTHROUGH) {
@@ -615,21 +582,21 @@ post_extract:
 	}
 
 	/* we need the username to ask for auth methods */
-	if (!urldata.username) {
+	if (!s->url.username) {
 		if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
 			goto done;
 
-		urldata.username = git__strdup(((git_credential_username *) cred)->username);
+		s->url.username = git__strdup(((git_credential_username *) cred)->username);
 		cred->free(cred);
 		cred = NULL;
-		if (!urldata.username)
+		if (!s->url.username)
 			goto done;
-	} else if (urldata.username && urldata.password) {
-		if ((error = git_credential_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0)
+	} else if (s->url.username && s->url.password) {
+		if ((error = git_credential_userpass_plaintext_new(&cred, s->url.username, s->url.password)) < 0)
 			goto done;
 	}
 
-	if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
+	if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
 		goto done;
 
 	error = GIT_EAUTH;
@@ -643,10 +610,10 @@ post_extract:
 			cred = NULL;
 		}
 
-		if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0)
+		if ((error = request_creds(&cred, t, s->url.username, auth_methods)) < 0)
 			goto done;
 
-		if (strcmp(urldata.username, git_credential_get_username(cred))) {
+		if (strcmp(s->url.username, git_credential_get_username(cred))) {
 			git_error_set(GIT_ERROR_SSH, "username does not match previous request");
 			error = -1;
 			goto done;
@@ -656,7 +623,7 @@ post_extract:
 
 		if (error == GIT_EAUTH) {
 			/* refresh auth methods */
-			if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
+			if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
 				goto done;
 			else
 				error = GIT_EAUTH;
@@ -691,8 +658,6 @@ done:
 	if (cred)
 		cred->free(cred);
 
-	git_net_url_dispose(&urldata);
-
 	return error;
 }