Module Name:    src
Committed By:   christos
Date:           Wed Dec 16 21:11:47 UTC 2015

Modified Files:
        src/usr.bin/ftp: fetch.c

Log Message:
PR/50438: NONAKA Kimihiro: ftp(1): CONNECT method support
Please test!


To generate a diff of this commit:
cvs rdiff -u -r1.214 -r1.215 src/usr.bin/ftp/fetch.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/ftp/fetch.c
diff -u src/usr.bin/ftp/fetch.c:1.214 src/usr.bin/ftp/fetch.c:1.215
--- src/usr.bin/ftp/fetch.c:1.214	Wed Dec 16 14:17:16 2015
+++ src/usr.bin/ftp/fetch.c	Wed Dec 16 16:11:47 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: fetch.c,v 1.214 2015/12/16 19:17:16 christos Exp $	*/
+/*	$NetBSD: fetch.c,v 1.215 2015/12/16 21:11:47 christos Exp $	*/
 
 /*-
  * Copyright (c) 1997-2015 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: fetch.c,v 1.214 2015/12/16 19:17:16 christos Exp $");
+__RCSID("$NetBSD: fetch.c,v 1.215 2015/12/16 21:11:47 christos Exp $");
 #endif /* not lint */
 
 /*
@@ -184,6 +184,18 @@ initurlinfo(struct urlinfo *ui)
 	ui->portnum = 0;
 }
 
+#ifdef WITH_SSL
+static void
+copyurlinfo(struct urlinfo *dui, struct urlinfo *sui)
+{
+	dui->host = ftp_strdup(sui->host);
+	dui->port = ftp_strdup(sui->port);
+	dui->path = ftp_strdup(sui->path);
+	dui->utype = sui->utype;
+	dui->portnum = sui->portnum;
+}
+#endif
+
 static void
 freeurlinfo(struct urlinfo *ui)
 {
@@ -721,10 +733,9 @@ print_cache(FETCH *fin, int isproxy)
 }
 
 static int
-print_get(FETCH *fin, int isproxy, const struct urlinfo *ui)
+print_get(FETCH *fin, int hasleading, int isproxy, const struct urlinfo *ui)
 {
-	int hasleading = 0;
-	const char *leading = "  (";
+	const char *leading = hasleading ? ", " : "  (";
 
 	if (isproxy) {
 		if (verbose) {
@@ -807,66 +818,87 @@ print_proxy(FETCH *fin, const char *lead
 #define C_OK 0
 #define C_CLEANUP 1
 #define C_IMPROPER 2
-
+#define C_PROXY 3
+#define C_NOPROXY 4
 
 static int
-negotiate_connection(FETCH *fin, const char *url, const char *penv,
-    off_t *rangestart, off_t *rangeend, off_t *entitylen,
-    time_t *mtime, struct authinfo *wauth, struct authinfo *pauth,
-    int *rval, int *ischunked)
+getresponseline(FETCH *fin, char *buf, size_t buflen, int *len)
 {
-	int			len, hcode, rv;
-	char			buf[FTPBUFLEN], *ep;
-	const char		*errormsg, *cp, *token;
-	char 			*location, *message, *auth;
-
-	auth = message = location = NULL;
+	const char *errormsg;
 
-	/* Read the response */
 	alarmtimer(quit_time ? quit_time : 60);
-	len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
+	*len = fetch_getline(fin, buf, buflen, &errormsg);
 	alarmtimer(0);
-	if (len < 0) {
+	if (*len < 0) {
 		if (*errormsg == '\n')
 			errormsg++;
 		warnx("Receiving HTTP reply: %s", errormsg);
-		goto cleanup_fetch_url;
+		return C_CLEANUP;
 	}
-	while (len > 0 && (ISLWS(buf[len-1])))
-		buf[--len] = '\0';
+	while (*len > 0 && (ISLWS(buf[*len-1])))
+		buf[--*len] = '\0';
 
-	DPRINTF("%s: received `%s'\n", __func__, buf);
+	if (*len)
+		DPRINTF("%s: received `%s'\n", __func__, buf);
+	return C_OK;
+}
+
+static int
+getresponse(FETCH *fin, char **cp, size_t buflen, int *hcode)
+{
+	int len, rv;
+	char *ep, *buf = *cp;
+
+	*hcode = 0;
+	if ((rv = getresponseline(fin, buf, buflen, &len)) != C_OK)
+		return rv;
 
 	/* Determine HTTP response code */
-	cp = strchr(buf, ' ');
-	if (cp == NULL)
-		goto improper;
-	else
-		cp++;
+	*cp = strchr(buf, ' ');
+	if (*cp == NULL)
+		return C_IMPROPER;
 
-	hcode = strtol(cp, &ep, 10);
+	(*cp)++;
+
+	*hcode = strtol(*cp, &ep, 10);
 	if (*ep != '\0' && !isspace((unsigned char)*ep))
-		goto improper;
+		return C_IMPROPER;
+
+	return C_OK;
+}
+
+static int
+negotiate_connection(FETCH *fin, const char *url, const char *penv,
+    off_t *rangestart, off_t *rangeend, off_t *entitylen,
+    time_t *mtime, struct authinfo *wauth, struct authinfo *pauth,
+    int *rval, int *ischunked, char **auth)
+{
+	int			len, hcode, rv;
+	char			buf[FTPBUFLEN], *ep;
+	const char		*cp, *token;
+	char 			*location, *message;
 
-	message = ftp_strdup(cp);
+	*auth = message = location = NULL;
+
+	/* Read the response */
+	ep = buf;
+	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
+	case C_CLEANUP:
+		goto cleanup_fetch_url;
+	case C_IMPROPER:
+		goto improper;
+	case C_OK:
+		message = ftp_strdup(ep);
+		break;
+	}
 
 	/* Read the rest of the header. */
 
 	for (;;) {
-		alarmtimer(quit_time ? quit_time : 60);
-		len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
-		alarmtimer(0);
-		if (len < 0) {
-			if (*errormsg == '\n')
-				errormsg++;
-			warnx("Receiving HTTP reply: %s", errormsg);
+		if ((rv = getresponseline(fin, buf, sizeof(buf), &len)) != C_OK)
 			goto cleanup_fetch_url;
-		}
-		while (len > 0 && (ISLWS(buf[len-1])))
-			buf[--len] = '\0';
 		if (len == 0)
 			break;
-		DPRINTF("%s: received `%s'\n", __func__, buf);
 
 	/*
 	 * Look for some headers
@@ -960,8 +992,8 @@ negotiate_connection(FETCH *fin, const c
 				    "scheme `%s'\n", __func__, token);
 				continue;
 			}
-			FREEPTR(auth);
-			auth = ftp_strdup(token);
+			FREEPTR(*auth);
+			*auth = ftp_strdup(token);
 			DPRINTF("%s: parsed auth as `%s'\n",
 			    __func__, cp);
 		}
@@ -1021,7 +1053,7 @@ negotiate_connection(FETCH *fin, const c
 		if (verbose || aauth.auth == NULL ||
 		    aauth.user == NULL || aauth.pass == NULL)
 			fprintf(ttyout, "%s\n", message);
-		if (EMPTYSTRING(auth)) {
+		if (EMPTYSTRING(*auth)) {
 			warnx(
 		    "No authentication challenge provided by server");
 			goto cleanup_fetch_url;
@@ -1043,7 +1075,7 @@ negotiate_connection(FETCH *fin, const c
 		}
 
 		authp = &aauth.auth;
-		if (auth_url(auth, authp, &aauth) == 0) {
+		if (auth_url(*auth, authp, &aauth) == 0) {
 			*rval = fetch_url(url, penv,
 			    pauth->auth, wauth->auth);
 			memset(*authp, 0, strlen(*authp));
@@ -1069,12 +1101,143 @@ improper:
 	rv = C_IMPROPER;
 	goto out;
 out:
-	FREEPTR(auth);
 	FREEPTR(message);
 	FREEPTR(location);
 	return rv;
 }		/* end of ftp:// or http:// specific setup */
 
+#ifdef WITH_SSL
+static int
+connectmethod(int s, FETCH *fin, struct urlinfo *oui, struct urlinfo *ui,
+    struct authinfo *wauth, struct authinfo *pauth,
+    char **auth, int *hasleading)
+{
+	void *ssl;
+	int hcode, rv;
+	const char *leading = *hasleading ? ", " : "  (";
+	const char *cp;
+	char buf[FTPBUFLEN], *ep;
+	char *message = NULL;
+
+	if (strchr(oui->host, ':')) {
+		char *h, *p;
+
+		/*
+		 * strip off IPv6 scope identifier,
+		 * since it is local to the node
+		 */
+		h = ftp_strdup(oui->host);
+		if (isipv6addr(h) && (p = strchr(h, '%')) != NULL) {
+			*p = '\0';
+		}
+		fetch_printf(fin, "CONNECT [%s]:%s HTTP/1.1\r\n",
+		    h, oui->port);
+		fetch_printf(fin, "Host: [%s]:%s\r\n", h, oui->port);
+		free(h);
+	} else {
+		fetch_printf(fin, "CONNECT %s:%s HTTP/1.1\r\n",
+		    oui->host, oui->port);
+		fetch_printf(fin, "Host: %s:%s\r\n",
+		    oui->host, oui->port);
+	}
+
+	print_agent(fin);
+	*hasleading = print_proxy(fin, leading, wauth->auth, pauth->auth);
+
+	if (verbose) {
+		leading = ", ";
+		(*hasleading)++;
+	} else {
+		leading = "  (";
+		*hasleading = 0;
+	}
+	if (pauth->auth) {
+		if (verbose) {
+			fprintf(ttyout, "%swith proxy authorization" , leading);
+			leading = ", ";
+			(*hasleading)++;
+		}
+		fetch_printf(fin, "Proxy-Authorization: %s\r\n", pauth->auth);
+	}
+
+	if (verbose && *hasleading)
+		fputs(")\n", ttyout);
+	leading = "  (";
+	*hasleading = 0;
+
+	fetch_printf(fin, "\r\n");
+	if (fetch_flush(fin) == EOF) {
+		warn("Writing HTTP request");
+		alarmtimer(0);
+		goto cleanup_fetch_url;
+	}
+	alarmtimer(0);
+
+	/* Read the response */
+	ep = buf;
+	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
+	case C_CLEANUP:
+		goto cleanup_fetch_url;
+	case C_IMPROPER:
+		goto improper;
+	case C_OK:
+		message = ftp_strdup(ep);
+		break;
+	}
+		
+	for (;;) {
+		int len;
+		if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK)
+			goto cleanup_fetch_url;
+		if (len == 0)
+			break;
+		if (match_token(&cp, "Proxy-Authenticate:")) {
+			const char *token;
+			if (!(token = match_token(&cp, "Basic"))) {
+				DPRINTF(
+				    "%s: skipping unknown auth scheme `%s'\n",
+				    __func__, token);
+				continue;
+			}
+			FREEPTR(*auth);
+			*auth = ftp_strdup(token);
+			DPRINTF("%s: parsed auth as " "`%s'\n", __func__, cp);
+		}
+	}
+
+	/* finished parsing header */
+	switch (hcode) {
+	case 200:
+		break;
+	default:
+		if (message)
+			warnx("Error proxy connect " "`%s'", message);
+		else
+			warnx("Unknown error proxy " "connect");
+		goto cleanup_fetch_url;
+	}
+
+	if ((ssl = fetch_start_ssl(s, ui->host)) == NULL)
+		goto cleanup_fetch_url;
+	fetch_set_ssl(fin, ssl);
+
+	FREEPTR(ui->host);
+	FREEPTR(ui->port);
+	oui->host = ftp_strdup(ui->host);
+	oui->port = ftp_strdup(ui->port);
+	rv = C_OK;
+	goto out;
+improper:
+	rv = C_IMPROPER;
+	goto out;
+cleanup_fetch_url:
+	rv = C_CLEANUP;
+	goto out;
+out:
+	FREEPTR(message);
+	return rv;
+}
+#endif
 
 /*
  * Retrieve URL, via a proxy if necessary, using HTTP.
@@ -1100,8 +1263,8 @@ fetch_url(const char *url, const char *p
 	static char		*xferbuf;
 	const char		*cp;
 	char			*ep;
+	char			*auth;
 	char			*volatile savefile;
-	char			*volatile auth;
 	char			*volatile location;
 	char			*volatile message;
 	char			*volatile decodedpath;
@@ -1114,6 +1277,9 @@ fetch_url(const char *url, const char *p
 	struct urlinfo		ui;
 	time_t			mtime;
 	void			*ssl = NULL;
+#ifdef WITH_SSL
+	struct urlinfo		oui;
+#endif
 
 	DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv));
 
@@ -1131,7 +1297,7 @@ fetch_url(const char *url, const char *p
 	initauthinfo(&wauth, wwwauth);
 	initauthinfo(&pauth, proxyauth);
 
-	 decodedpath = NULL;
+	decodedpath = NULL;
 
 	if (sigsetjmp(httpabort, 1))
 		goto cleanup_fetch_url;
@@ -1139,6 +1305,13 @@ fetch_url(const char *url, const char *p
 	if (parse_url(url, "URL", &ui, &wauth) == -1)
 		goto cleanup_fetch_url;
 
+#ifdef WITH_SSL
+	if (ui.utype == HTTPS_URL_T)
+		copyurlinfo(&oui, &ui);
+	else
+		initurlinfo(&oui);
+#endif
+
 	if (ui.utype == FILE_URL_T && ! EMPTYSTRING(ui.host)
 	    && strcasecmp(ui.host, "localhost") != 0) {
 		warnx("No support for non local file URL `%s'", url);
@@ -1270,7 +1443,24 @@ fetch_url(const char *url, const char *p
 		if (verbose)
 			fprintf(ttyout, "Requesting %s\n", url);
 
-		hasleading = print_get(fin, isproxy, &ui);
+		hasleading = 0;
+#ifdef WITH_SSL
+		if (isproxy && oui.utype == HTTPS_URL_T) {
+			switch (connectmethod(s, fin, &oui, &ui, &wauth, &pauth,
+			    &auth, &hasleading)) {
+			case C_CLEANUP:
+				goto cleanup_fetch_url;
+			case C_IMPROPER:
+				goto improper;
+			case C_OK:
+				break;
+			default:
+				abort();
+			}
+		}
+#endif
+
+		hasleading = print_get(fin, hasleading, isproxy, &ui);
 		if (hasleading)
 			leading = ", ";
 		else
@@ -1280,7 +1470,8 @@ fetch_url(const char *url, const char *p
 			print_cache(fin, isproxy);
 
 		print_agent(fin);
-		hasleading = print_proxy(fin, leading, wauth.auth, pauth.auth);
+		hasleading = print_proxy(fin, leading, wauth.auth,
+		     auth ? NULL : pauth.auth);
 		if (hasleading) {
 			leading = ", ";
 			if (verbose)
@@ -1297,7 +1488,7 @@ fetch_url(const char *url, const char *p
 
 		switch (negotiate_connection(fin, url, penv,
 		    &rangestart, &rangeend, &entitylen,
-		    &mtime, &wauth, &pauth, &rval, &ischunked)) {
+		    &mtime, &wauth, &pauth, &rval, &ischunked, &auth)) {
 		case C_OK:
 			break;
 		case C_CLEANUP:
@@ -1547,6 +1738,9 @@ fetch_url(const char *url, const char *p
 	if (savefile != outfile)
 		FREEPTR(savefile);
 	freeurlinfo(&ui);
+#ifdef WITH_SSL
+	freeurlinfo(&oui);
+#endif
 	freeauthinfo(&wauth);
 	freeauthinfo(&pauth);
 	FREEPTR(decodedpath);

Reply via email to