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);