=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ftp/fetch.c,v retrieving revision 1.60 retrieving revision 1.61 diff -c -r1.60 -r1.61 *** src/usr.bin/ftp/fetch.c 2006/04/25 05:45:20 1.60 --- src/usr.bin/ftp/fetch.c 2006/05/16 16:20:42 1.61 *************** *** 1,4 **** ! /* $OpenBSD: fetch.c,v 1.60 2006/04/25 05:45:20 tedu Exp $ */ /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ /*- --- 1,4 ---- ! /* $OpenBSD: fetch.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $ */ /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ /*- *************** *** 38,44 **** */ #if !defined(lint) && !defined(SMALL) ! static const char rcsid[] = "$OpenBSD: fetch.c,v 1.60 2006/04/25 05:45:20 tedu Exp $"; #endif /* not lint and not SMALL */ /* --- 38,44 ---- */ #if !defined(lint) && !defined(SMALL) ! static const char rcsid[] = "$OpenBSD: fetch.c,v 1.61 2006/05/16 16:20:42 deraadt Exp $"; #endif /* not lint and not SMALL */ /* *************** *** 63,74 **** --- 63,82 ---- #include #include #include + #include #include #include #include #include #include + #ifndef SMALL + #include + #include + #else + #define SSL void + #endif + #include "ftp_var.h" static int url_get(const char *, const char *, const char *); *************** *** 76,84 **** --- 84,101 ---- void abortfile(int); char hextochar(const char *); char *urldecode(const char *); + int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4))); + char *ftp_readline(FILE *, SSL *, size_t *); + int ftp_read(FILE *, SSL *, char *, size_t); + #ifndef SMALL + int proxy_connect(int, char *); + int SSL_vprintf(SSL *, const char *, va_list); + char *SSL_readline(SSL *, size_t *); + #endif #define FTP_URL "ftp://" /* ftp URL prefix */ #define HTTP_URL "http://" /* http URL prefix */ + #define HTTPS_URL "https://" /* https URL prefix */ #define FILE_URL "file:" /* file URL prefix */ #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ #define HTTP_PROXY "http_proxy" /* env var with http proxy location */ *************** *** 111,118 **** volatile sig_t oldintr; FILE *fin = NULL; off_t hashbytes; - size_t len; const char *errstr; line = strdup(origline); if (line == NULL) --- 128,141 ---- volatile sig_t oldintr; FILE *fin = NULL; off_t hashbytes; const char *errstr; + size_t len, wlen; + #ifndef SMALL + char *sslpath = NULL, *sslhost = NULL; + int ishttpsurl = 0; + SSL_CTX *ssl_ctx = NULL; + #endif + SSL *ssl = NULL; line = strdup(origline); if (line == NULL) *************** *** 125,130 **** --- 148,158 ---- } else if (strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { host = line + sizeof(FILE_URL) - 1; isfileurl = 1; + #ifndef SMALL + } else if (strncasecmp(line, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) { + host = line + sizeof(HTTPS_URL) - 1; + ishttpsurl = 1; + #endif } else errx(1, "url_get: Invalid URL '%s'", line); *************** *** 160,165 **** --- 188,201 ---- } if (!isfileurl && proxyenv != NULL) { /* use proxy */ + #ifndef SMALL + if (ishttpsurl) { + sslpath = strdup(path); + sslhost = strdup(host); + if (! sslpath || ! sslhost) + errx(1, "Can't allocate memory for https path/host."); + } + #endif proxy = strdup(proxyenv); if (proxy == NULL) errx(1, "Can't allocate memory for proxy URL."); *************** *** 280,294 **** memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; port = portnum ? portnum : httpport; error = getaddrinfo(host, port, &hints, &res0); if (error == EAI_SERVICE && port == httpport) { - /* - * If the services file is corrupt/missing, fall back - * on our hard-coded defines. - */ snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); error = getaddrinfo(host, pbuf, &hints, &res0); } if (error) { warnx("%s: %s", gai_strerror(error), host); --- 316,339 ---- memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; + #ifndef SMALL + port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); + #else port = portnum ? portnum : httpport; + #endif error = getaddrinfo(host, port, &hints, &res0); + /* + * If the services file is corrupt/missing, fall back + * on our hard-coded defines. + */ if (error == EAI_SERVICE && port == httpport) { snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); error = getaddrinfo(host, pbuf, &hints, &res0); + #ifndef SMALL + } else if (error == EAI_SERVICE && port == httpsport) { + snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); + error = getaddrinfo(host, pbuf, &hints, &res0); + #endif } if (error) { warnx("%s: %s", gai_strerror(error), host); *************** *** 330,335 **** --- 375,384 ---- else port = NULL; + #ifndef SMALL + if (proxyenv && sslhost) + proxy_connect(s, sslhost); + #endif break; } freeaddrinfo(res0); *************** *** 338,344 **** --- 387,422 ---- goto cleanup_url_get; } + #ifndef SMALL + if (ishttpsurl) { + if (proxyenv && sslpath) { + ishttpsurl = 0; + proxy = NULL; + path = sslpath; + } + SSL_library_init(); + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + ssl = SSL_new(ssl_ctx); + if (ssl == NULL || ssl_ctx == NULL) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get; + } + if (SSL_set_fd(ssl, s) == 0) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get; + } + if (SSL_connect(ssl) <= 0) { + ERR_print_errors_fp(ttyout); + goto cleanup_url_get;; + } + } else { + fin = fdopen(s, "r+"); + } + #else fin = fdopen(s, "r+"); + #endif if (verbose) fprintf(ttyout, "Requesting %s", origline); *************** *** 352,361 **** * Host: directive must use the destination host address for * the original URI (path). We do not attach it at this moment. */ ! fprintf(fin, "GET %s HTTP/1.0\r\n%s\r\n\r\n", path, HTTP_USER_AGENT); } else { ! fprintf(fin, "GET /%s HTTP/1.0\r\nHost: ", path); if (strchr(host, ':')) { char *h, *p; --- 430,439 ---- * Host: directive must use the destination host address for * the original URI (path). We do not attach it at this moment. */ ! ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s\r\n\r\n", path, HTTP_USER_AGENT); } else { ! ftp_printf(fin, ssl, "GET /%s HTTP/1.0\r\nHost: ", path); if (strchr(host, ':')) { char *h, *p; *************** *** 368,395 **** errx(1, "Can't allocate memory."); if ((p = strchr(h, '%')) != NULL) *p = '\0'; ! fprintf(fin, "[%s]", h); free(h); } else ! fprintf(fin, "%s", host); /* * Send port number only if it's specified and does not equal * 80. Some broken HTTP servers get confused if you explicitly * send them the port number. */ if (port && strcmp(port, "80") != 0) ! fprintf(fin, ":%s", port); ! fprintf(fin, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); if (verbose) fprintf(ttyout, "\n"); } ! if (fflush(fin) == EOF) { warn("Writing HTTP request"); goto cleanup_url_get; } ! ! if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } --- 446,477 ---- errx(1, "Can't allocate memory."); if ((p = strchr(h, '%')) != NULL) *p = '\0'; ! ftp_printf(fin, ssl, "[%s]", h); free(h); } else ! ftp_printf(fin, ssl, "%s", host); /* * Send port number only if it's specified and does not equal * 80. Some broken HTTP servers get confused if you explicitly * send them the port number. */ + #ifndef SMALL + if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0) + ftp_printf(fin, ssl, ":%s", port); + #else if (port && strcmp(port, "80") != 0) ! ftp_printf(fin, ssl, ":%s", port); ! #endif ! ftp_printf(fin, ssl, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); if (verbose) fprintf(ttyout, "\n"); } ! if (fin != NULL && fflush(fin) == EOF) { warn("Writing HTTP request"); goto cleanup_url_get; } ! if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } *************** *** 422,431 **** filesize = -1; while (1) { ! if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) buf[--len] = '\0'; if (len == 0) --- 504,514 ---- filesize = -1; while (1) { ! if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { warn("Receiving HTTP reply"); goto cleanup_url_get; } + while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) buf[--len] = '\0'; if (len == 0) *************** *** 490,499 **** if ((buf = malloc(4096)) == NULL) errx(1, "Can't allocate memory for transfer buffer"); i = 0; ! while ((len = fread(buf, sizeof(char), 4096, fin)) > 0) { bytes += len; ! for (cp = buf; len > 0; len -= i, cp += i) { ! if ((i = write(out, cp, len)) == -1) { warn("Writing %s", savefile); goto cleanup_url_get; } --- 573,584 ---- if ((buf = malloc(4096)) == NULL) errx(1, "Can't allocate memory for transfer buffer"); i = 0; ! len = 1; ! while (len > 0) { ! len = ftp_read(fin, ssl, buf, 4096); bytes += len; ! for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { ! if ((i = write(out, cp, wlen)) == -1) { warn("Writing %s", savefile); goto cleanup_url_get; } *************** *** 541,546 **** --- 626,637 ---- warnx("Improper response from %s", host); cleanup_url_get: + #ifndef SMALL + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + } + #endif if (fin != NULL) fclose(fin); else if (s != -1) *************** *** 641,646 **** --- 732,741 ---- * Try HTTP URL-style arguments first. */ if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || + #ifndef SMALL + /* even if we compiled without SSL, url_get will check */ + strncasecmp(line, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || + #endif strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { redirect_loop = 0; if (url_get(line, httpproxy, outfile) == -1) *************** *** 881,888 **** *ret = ' '; continue; } ! /* Can't use strtol here because next char after %xx may be ! * a digit. */ if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { *ret = hextochar(&str[i+1]); i+=2; --- 976,985 ---- *ret = ' '; continue; } ! ! /* Cannot use strtol here because next char ! * after %xx may be a digit. ! */ if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { *ret = hextochar(&str[i+1]); i+=2; *************** *** 923,930 **** --- 1020,1151 ---- if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || + #ifndef SMALL + strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || + #endif strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || strstr(p, ":/")) return (1); return (0); } + + char * + ftp_readline(FILE *fp, SSL *ssl, size_t *lenp) + { + if (fp != NULL) + return fparseln(fp, lenp, NULL, "\0\0\0", 0); + #ifndef SMALL + else if (ssl != NULL) + return SSL_readline(ssl, lenp); + #endif + else + return NULL; + } + + int + ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len) + { + int ret; + if (fp != NULL) + ret = fread(buf, sizeof(char), len, fp); + #ifndef SMALL + else if (ssl != NULL) + ret = SSL_read(ssl, buf, (int)len); + #endif + else + ret = 0; + return (ret); + } + + int + ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...) + { + int ret; + va_list ap; + + va_start(ap, fmt); + + if (fp != NULL) + ret = vfprintf(fp, fmt, ap); + #ifndef SMALL + else if (ssl != NULL) + ret = SSL_vprintf((SSL*)ssl, fmt, ap); + #endif + else + ret = NULL; + + va_end(ap); + return (ret); + } + + #ifndef SMALL + int + SSL_vprintf(SSL *ssl, const char *fmt, va_list ap) + { + int ret; + char *string; + + if ((ret = vasprintf(&string, fmt, ap)) == -1) + return ret; + ret = SSL_write(ssl, (void *)string, (int)strlen(string)); + free(string); + return ret; + } + + char * + SSL_readline(SSL *ssl, size_t *lenp) + { + int i; + size_t len; + char *buf, *q, c; + + len = 128; + if ((buf = malloc(len)) == NULL) + errx(1, "Can't allocate memory for transfer buffer"); + for (i = 0; ; i++) { + if (i >= len - 1) { + if ((q = realloc(buf, 2 * len)) == NULL) + errx(1, "Can't expand transfer buffer"); + buf = q; + len *= 2; + } + if (SSL_read(ssl, &c, 1) <= 0) + break; + buf[i] = c; + if (c == '\n') + break; + } + *lenp = i; + return (buf); + } + + int + proxy_connect(int socket, char *host) + { + char buf[1024]; + char *connstr, *hosttail, *port; + + if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && + (hosttail[1] == '\0' || hosttail[1] == ':')) { + host++; + *hosttail++ = '\0'; + } else + hosttail = host; + + port = strrchr(hosttail, ':'); /* find portnum */ + if (port != NULL) + *port++ = '\0'; + if (!port) + port = "443"; + + if (debug) + printf("CONNECT %s:%s HTTP/1.1\n\n", host, port); + asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\n\n", host, port); + if (!connstr) + errx(1, "Could not allocate memory to assemble connect string!"); + if (write(socket, connstr, strlen(connstr)) != strlen(connstr)) + errx(1, "Could not send connect string: %s", strerror(errno)); + read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ + return(200); + } + #endif