diff options
Diffstat (limited to 'src/fetch.c')
-rw-r--r-- | src/fetch.c | 187 |
1 files changed, 124 insertions, 63 deletions
diff --git a/src/fetch.c b/src/fetch.c index 91b49fd2435a..e9589e044ed1 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -1,5 +1,5 @@ -/* $NetBSD: fetch.c,v 1.18 2009/11/15 10:12:37 lukem Exp $ */ -/* from NetBSD: fetch.c,v 1.191 2009/08/17 09:08:16 christos Exp */ +/* $NetBSD: fetch.c,v 1.19 2013/05/05 11:17:30 lukem Exp $ */ +/* from NetBSD: fetch.c,v 1.202 2013/02/23 13:47:36 christos Exp */ /*- * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. @@ -39,7 +39,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID(" NetBSD: fetch.c,v 1.191 2009/08/17 09:08:16 christos Exp "); +__RCSID(" NetBSD: fetch.c,v 1.202 2013/02/23 13:47:36 christos Exp "); #endif /* not lint */ /* @@ -57,6 +57,7 @@ __RCSID(" NetBSD: fetch.c,v 1.191 2009/08/17 09:08:16 christos Exp "); #include <arpa/ftp.h> #include <arpa/inet.h> +#include <assert.h> #include <ctype.h> #include <err.h> #include <errno.h> @@ -70,18 +71,22 @@ __RCSID(" NetBSD: fetch.c,v 1.191 2009/08/17 09:08:16 christos Exp "); #endif /* tnftp */ +#include "ssl.h" #include "ftp_var.h" #include "version.h" typedef enum { UNKNOWN_URL_T=-1, HTTP_URL_T, +#ifdef WITH_SSL + HTTPS_URL_T, +#endif FTP_URL_T, FILE_URL_T, CLASSIC_URL_T } url_t; -void aborthttp(int); +__dead static void aborthttp(int); #ifndef NO_AUTH static int auth_url(const char *, char **, const char *, const char *); static void base64_encode(const unsigned char *, size_t, unsigned char *); @@ -106,7 +111,15 @@ static int redirect_loop; #define FILE_URL "file://" /* file URL prefix */ #define FTP_URL "ftp://" /* ftp URL prefix */ #define HTTP_URL "http://" /* http URL prefix */ +#ifdef WITH_SSL +#define HTTPS_URL "https://" /* https URL prefix */ +#define IS_HTTP_TYPE(urltype) \ + (((urltype) == HTTP_URL_T) || ((urltype) == HTTPS_URL_T)) +#else +#define IS_HTTP_TYPE(urltype) \ + ((urltype) == HTTP_URL_T) +#endif /* * Determine if token is the next word in buf (case insensitive). @@ -288,7 +301,7 @@ url_decode(char *url) /* - * Parse URL of form (per RFC3986): + * Parse URL of form (per RFC 3986): * <type>://[<user>[:<password>]@]<host>[:<port>][/<path>] * Returns -1 if a parse error occurred, otherwise 0. * It's the caller's responsibility to url_decode() the returned @@ -298,7 +311,7 @@ url_decode(char *url) * malloc(3)ed strings of the relevant section, and port to * the number given, or ftpport if ftp://, or httpport if http://. * - * XXX: this is not totally RFC3986 compliant; <path> will have the + * XXX: this is not totally RFC 3986 compliant; <path> will have the * leading `/' unless it's an ftp:// URL, as this makes things easier * for file:// and http:// URLs. ftp:// URLs have the `/' between the * host and the URL-path removed, but any additional leading slashes @@ -352,6 +365,13 @@ parse_url(const char *url, const char *desc, url_t *utype, } else if (STRNEQUAL(url, FILE_URL)) { url += sizeof(FILE_URL) - 1; *utype = FILE_URL_T; +#ifdef WITH_SSL + } else if (STRNEQUAL(url, HTTPS_URL)) { + url += sizeof(HTTPS_URL) - 1; + *utype = HTTPS_URL_T; + *portnum = HTTPS_PORT; + tport = httpsport; +#endif } else { warnx("Invalid %s `%s'", desc, url); cleanup_parse_url: @@ -401,7 +421,7 @@ parse_url(const char *url, const char *desc, url_t *utype, #ifdef INET6 /* * Check if thost is an encoded IPv6 address, as per - * RFC3986: + * RFC 3986: * `[' ipv6-address ']' */ if (*thost == '[') { @@ -469,7 +489,7 @@ sigjmp_buf httpabort; /* * Retrieve URL, via a proxy if necessary, using HTTP. * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or - * http_proxy as appropriate. + * http_proxy/https_proxy as appropriate. * Supports HTTP redirects. * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection * is still open (e.g, ftp xfer with trailing /) @@ -504,17 +524,21 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) char *puser, *ppass, *useragent; off_t hashbytes, rangestart, rangeend, entitylen; int (*volatile closefunc)(FILE *); - FILE *volatile fin; + FETCH *volatile fin; FILE *volatile fout; time_t mtime; url_t urltype; in_port_t portnum; +#ifdef WITH_SSL + void *ssl; +#endif DPRINTF("fetch_url: `%s' proxyenv `%s'\n", url, STRorNULL(proxyenv)); oldintr = oldintp = NULL; closefunc = NULL; - fin = fout = NULL; + fin = NULL; + fout = NULL; s = -1; savefile = NULL; auth = location = message = NULL; @@ -537,7 +561,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) rval = fetch_ftp(url); goto cleanup_fetch_url; } - if (urltype != HTTP_URL_T || outfile == NULL) { + if (!IS_HTTP_TYPE(urltype) || outfile == NULL) { warnx("Invalid URL (no file after host) `%s'", url); goto cleanup_fetch_url; } @@ -577,17 +601,17 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) } if (urltype == FILE_URL_T) { /* file:// URLs */ direction = "copied"; - fin = fopen(decodedpath, "r"); + fin = fetch_open(decodedpath, "r"); if (fin == NULL) { warn("Can't open `%s'", decodedpath); goto cleanup_fetch_url; } - if (fstat(fileno(fin), &sb) == 0) { + if (fstat(fetch_fileno(fin), &sb) == 0) { mtime = sb.st_mtime; filesize = sb.st_size; } if (restart_point) { - if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { + if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) < 0) { warn("Can't seek to restart `%s'", decodedpath); goto cleanup_fetch_url; @@ -600,12 +624,19 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) (LLT)restart_point); fputs("\n", ttyout); } + if (0 == rcvbuf_size) { + rcvbuf_size = 8 * 1024; /* XXX */ + } } else { /* ftp:// or http:// URLs */ const char *leading; int hasleading; if (proxyenv == NULL) { - if (urltype == HTTP_URL_T) +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) + proxyenv = getoptionvalue("https_proxy"); +#endif + if (proxyenv == NULL && IS_HTTP_TYPE(urltype)) proxyenv = getoptionvalue("http_proxy"); else if (urltype == FTP_URL_T) proxyenv = getoptionvalue("ftp_proxy"); @@ -666,7 +697,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) &ppath) == -1) goto cleanup_fetch_url; - if ((purltype != HTTP_URL_T + if ((!IS_HTTP_TYPE(purltype) && purltype != FTP_URL_T) || EMPTYSTRING(phost) || (! EMPTYSTRING(ppath) @@ -696,6 +727,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) FREEPTR(path); path = ftp_strdup(url); FREEPTR(ppath); + urltype = purltype; } } /* ! EMPTYSTRING(proxyenv) */ @@ -706,7 +738,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) hints.ai_protocol = 0; error = getaddrinfo(host, port, &hints, &res0); if (error) { - warnx("Can't lookup `%s:%s': %s", host, port, + warnx("Can't LOOKUP `%s:%s': %s", host, port, (error == EAI_SYSTEM) ? strerror(errno) : gai_strerror(error)); goto cleanup_fetch_url; @@ -715,6 +747,9 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) host = res0->ai_canonname; s = -1; +#ifdef WITH_SSL + ssl = NULL; +#endif for (res = res0; res; res = res->ai_next) { char hname[NI_MAXHOST], sname[NI_MAXSERV]; @@ -740,12 +775,23 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) continue; } - if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { close(s); s = -1; continue; } +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) { + if ((ssl = fetch_start_ssl(s)) == NULL) { + close(s); + s = -1; + continue; + } + } +#endif + /* success */ break; } @@ -755,7 +801,9 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) goto cleanup_fetch_url; } - fin = fdopen(s, "r+"); + fin = fetch_fdopen(s, "r+"); + fetch_set_ssl(fin, ssl); + /* * Construct and send the request. */ @@ -770,11 +818,11 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) leading = ", "; hasleading++; } - fprintf(fin, "GET %s HTTP/1.0\r\n", path); + fetch_printf(fin, "GET %s HTTP/1.0\r\n", path); if (flushcache) - fprintf(fin, "Pragma: no-cache\r\n"); + fetch_printf(fin, "Pragma: no-cache\r\n"); } else { - fprintf(fin, "GET %s HTTP/1.1\r\n", path); + fetch_printf(fin, "GET %s HTTP/1.1\r\n", path); if (strchr(host, ':')) { char *h, *p; @@ -787,18 +835,23 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) (p = strchr(h, '%')) != NULL) { *p = '\0'; } - fprintf(fin, "Host: [%s]", h); + fetch_printf(fin, "Host: [%s]", h); free(h); } else - fprintf(fin, "Host: %s", host); + fetch_printf(fin, "Host: %s", host); +#ifdef WITH_SSL + if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) || + (urltype == HTTPS_URL_T && portnum != HTTPS_PORT)) +#else if (portnum != HTTP_PORT) - fprintf(fin, ":%u", portnum); - fprintf(fin, "\r\n"); - fprintf(fin, "Accept: */*\r\n"); - fprintf(fin, "Connection: close\r\n"); +#endif + fetch_printf(fin, ":%u", portnum); + fetch_printf(fin, "\r\n"); + fetch_printf(fin, "Accept: */*\r\n"); + fetch_printf(fin, "Connection: close\r\n"); if (restart_point) { fputs(leading, ttyout); - fprintf(fin, "Range: bytes=" LLF "-\r\n", + fetch_printf(fin, "Range: bytes=" LLF "-\r\n", (LLT)restart_point); fprintf(ttyout, "restarting at " LLF, (LLT)restart_point); @@ -806,12 +859,12 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) hasleading++; } if (flushcache) - fprintf(fin, "Cache-Control: no-cache\r\n"); + fetch_printf(fin, "Cache-Control: no-cache\r\n"); } if ((useragent=getenv("FTPUSERAGENT")) != NULL) { - fprintf(fin, "User-Agent: %s\r\n", useragent); + fetch_printf(fin, "User-Agent: %s\r\n", useragent); } else { - fprintf(fin, "User-Agent: %s/%s\r\n", + fetch_printf(fin, "User-Agent: %s/%s\r\n", FTP_PRODUCT, FTP_VERSION); } if (wwwauth) { @@ -821,7 +874,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) leading = ", "; hasleading++; } - fprintf(fin, "Authorization: %s\r\n", wwwauth); + fetch_printf(fin, "Authorization: %s\r\n", wwwauth); } if (proxyauth) { if (verbose) { @@ -830,18 +883,18 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) leading = ", "; hasleading++; } - fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth); + fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth); } if (verbose && hasleading) fputs(")\n", ttyout); - fprintf(fin, "\r\n"); - if (fflush(fin) == EOF) { + fetch_printf(fin, "\r\n"); + if (fetch_flush(fin) == EOF) { warn("Writing HTTP request"); goto cleanup_fetch_url; } /* Read the response */ - len = get_line(fin, buf, sizeof(buf), &errormsg); + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); if (len < 0) { if (*errormsg == '\n') errormsg++; @@ -865,7 +918,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) /* Read the rest of the header. */ while (1) { - len = get_line(fin, buf, sizeof(buf), &errormsg); + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); if (len < 0) { if (*errormsg == '\n') errormsg++; @@ -941,28 +994,18 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) } else if (match_token(&cp, "Last-Modified:")) { struct tm parsed; - char *t; + const char *t; memset(&parsed, 0, sizeof(parsed)); - /* RFC1123 */ - if ((t = strptime(cp, - "%a, %d %b %Y %H:%M:%S GMT", - &parsed)) - /* RFC0850 */ - || (t = strptime(cp, - "%a, %d-%b-%y %H:%M:%S GMT", - &parsed)) - /* asctime */ - || (t = strptime(cp, - "%a, %b %d %H:%M:%S %Y", - &parsed))) { + t = parse_rfc2616time(&parsed, cp); + if (t != NULL) { parsed.tm_isdst = -1; if (*t == '\0') mtime = timegm(&parsed); #ifndef NO_DEBUG if (ftp_debug && mtime != -1) { fprintf(ttyout, - "parsed date as: %s", + "parsed time as: %s", rfc2822time(localtime(&mtime))); } #endif @@ -1142,6 +1185,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) (void)xsignal(SIGQUIT, psummary); oldintr = xsignal(SIGINT, aborthttp); + assert(rcvbuf_size > 0); if ((size_t)rcvbuf_size > bufsize) { if (xferbuf) (void)free(xferbuf); @@ -1162,7 +1206,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) lastchunk = 0; /* read chunk-size */ if (ischunked) { - if (fgets(xferbuf, bufsize, fin) == NULL) { + if (fetch_getln(xferbuf, bufsize, fin) == NULL) { warnx("Unexpected EOF reading chunk-size"); goto cleanup_fetch_url; } @@ -1215,7 +1259,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) if (ischunked) bufrem = MIN(chunksize, bufrem); while (bufrem > 0) { - flen = fread(xferbuf, sizeof(char), + flen = fetch_read(xferbuf, sizeof(char), MIN((off_t)bufsize, bufrem), fin); if (flen <= 0) goto chunkdone; @@ -1254,7 +1298,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) /* read CRLF after chunk*/ chunkdone: if (ischunked) { - if (fgets(xferbuf, bufsize, fin) == NULL) { + if (fetch_getln(xferbuf, bufsize, fin) == NULL) { warnx("Unexpected EOF reading chunk CRLF"); goto cleanup_fetch_url; } @@ -1274,7 +1318,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) (void)putc('#', ttyout); (void)putc('\n', ttyout); } - if (ferror(fin)) { + if (fetch_error(fin)) { warn("Reading file"); goto cleanup_fetch_url; } @@ -1311,7 +1355,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) if (oldintp) (void)xsignal(SIGPIPE, oldintp); if (fin != NULL) - fclose(fin); + fetch_close(fin); else if (s != -1) close(s); if (closefunc != NULL && fout != NULL) @@ -1340,7 +1384,7 @@ fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) /* * Abort a HTTP retrieval */ -void +static void aborthttp(int notused) { char msgbuf[100]; @@ -1537,12 +1581,12 @@ fetch_ftp(const char *url) * directories in one step. * * If we are dealing with an `ftp://host/path' URL - * (urltype is FTP_URL_T), then RFC3986 says we need to + * (urltype is FTP_URL_T), then RFC 3986 says we need to * send a separate CWD command for each unescaped "/" * in the path, and we have to interpret %hex escaping * *after* we find the slashes. It's possible to get * empty components here, (from multiple adjacent - * slashes in the path) and RFC3986 says that we should + * slashes in the path) and RFC 3986 says that we should * still do `CWD ' (with a null argument) in such cases. * * Many ftp servers don't support `CWD ', so if there's an @@ -1614,8 +1658,8 @@ fetch_ftp(const char *url) fprintf(stderr, "\n" "ftp: The `CWD ' command (without a directory), which is required by\n" -" RFC3986 to support the empty directory in the URL pathname (`//'),\n" -" conflicts with the server's conformance to RFC0959.\n" +" RFC 3986 to support the empty directory in the URL pathname (`//'),\n" +" conflicts with the server's conformance to RFC 959.\n" " Try the same URL without the `//' in the URL pathname.\n" "\n"); goto cleanup_fetch_ftp; @@ -1702,6 +1746,7 @@ static int go_fetch(const char *url) { char *proxyenv; + char *p; #ifndef NO_ABOUT /* @@ -1742,10 +1787,26 @@ go_fetch(const char *url) /* * Check for file:// and http:// URLs. */ - if (STRNEQUAL(url, HTTP_URL) || STRNEQUAL(url, FILE_URL)) + if (STRNEQUAL(url, HTTP_URL) +#ifdef WITH_SSL + || STRNEQUAL(url, HTTPS_URL) +#endif + || STRNEQUAL(url, FILE_URL)) return (fetch_url(url, NULL, NULL, NULL)); /* + * If it contains "://" but does not begin with ftp:// + * or something that was already handled, then it's + * unsupported. + * + * If it contains ":" but not "://" then we assume the + * part before the colon is a host name, not an URL scheme, + * so we don't try to match that here. + */ + if ((p = strstr(url, "://")) != NULL && ! STRNEQUAL(url, FTP_URL)) + errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url); + + /* * Try FTP URL-style and host:file arguments next. * If ftpproxy is set with an FTP URL, use fetch_url() * Othewise, use fetch_ftp(). |