version 1.60, 2006/04/25 05:45:20 |
version 1.61, 2006/05/16 16:20:42 |
|
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <signal.h> |
#include <signal.h> |
#include <stdio.h> |
#include <stdio.h> |
|
#include <stdarg.h> |
#include <errno.h> |
#include <errno.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <util.h> |
#include <util.h> |
|
|
|
#ifndef SMALL |
|
#include <openssl/ssl.h> |
|
#include <openssl/err.h> |
|
#else |
|
#define SSL void |
|
#endif |
|
|
#include "ftp_var.h" |
#include "ftp_var.h" |
|
|
static int url_get(const char *, const char *, const char *); |
static int url_get(const char *, const char *, const char *); |
|
|
void abortfile(int); |
void abortfile(int); |
char hextochar(const char *); |
char hextochar(const char *); |
char *urldecode(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 FTP_URL "ftp://" /* ftp URL prefix */ |
#define HTTP_URL "http://" /* http URL prefix */ |
#define HTTP_URL "http://" /* http URL prefix */ |
|
#define HTTPS_URL "https://" /* https URL prefix */ |
#define FILE_URL "file:" /* file URL prefix */ |
#define FILE_URL "file:" /* file URL prefix */ |
#define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ |
#define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ |
#define HTTP_PROXY "http_proxy" /* env var with http proxy location */ |
#define HTTP_PROXY "http_proxy" /* env var with http proxy location */ |
|
|
volatile sig_t oldintr; |
volatile sig_t oldintr; |
FILE *fin = NULL; |
FILE *fin = NULL; |
off_t hashbytes; |
off_t hashbytes; |
size_t len; |
|
const char *errstr; |
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); |
line = strdup(origline); |
if (line == NULL) |
if (line == NULL) |
|
|
} else if (strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { |
} else if (strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { |
host = line + sizeof(FILE_URL) - 1; |
host = line + sizeof(FILE_URL) - 1; |
isfileurl = 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 |
} else |
errx(1, "url_get: Invalid URL '%s'", line); |
errx(1, "url_get: Invalid URL '%s'", line); |
|
|
|
|
} |
} |
|
|
if (!isfileurl && proxyenv != NULL) { /* use proxy */ |
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); |
proxy = strdup(proxyenv); |
if (proxy == NULL) |
if (proxy == NULL) |
errx(1, "Can't allocate memory for proxy URL."); |
errx(1, "Can't allocate memory for proxy URL."); |
|
|
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = family; |
hints.ai_family = family; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
|
#ifndef SMALL |
|
port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); |
|
#else |
port = portnum ? portnum : httpport; |
port = portnum ? portnum : httpport; |
|
#endif |
error = getaddrinfo(host, port, &hints, &res0); |
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) { |
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); |
snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); |
error = getaddrinfo(host, pbuf, &hints, &res0); |
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) { |
if (error) { |
warnx("%s: %s", gai_strerror(error), host); |
warnx("%s: %s", gai_strerror(error), host); |
|
|
else |
else |
port = NULL; |
port = NULL; |
|
|
|
#ifndef SMALL |
|
if (proxyenv && sslhost) |
|
proxy_connect(s, sslhost); |
|
#endif |
break; |
break; |
} |
} |
freeaddrinfo(res0); |
freeaddrinfo(res0); |
|
|
goto cleanup_url_get; |
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+"); |
fin = fdopen(s, "r+"); |
|
#endif |
|
|
if (verbose) |
if (verbose) |
fprintf(ttyout, "Requesting %s", origline); |
fprintf(ttyout, "Requesting %s", origline); |
|
|
* Host: directive must use the destination host address for |
* Host: directive must use the destination host address for |
* the original URI (path). We do not attach it at this moment. |
* 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, |
ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s\r\n\r\n", path, |
HTTP_USER_AGENT); |
HTTP_USER_AGENT); |
} else { |
} else { |
fprintf(fin, "GET /%s HTTP/1.0\r\nHost: ", path); |
ftp_printf(fin, ssl, "GET /%s HTTP/1.0\r\nHost: ", path); |
if (strchr(host, ':')) { |
if (strchr(host, ':')) { |
char *h, *p; |
char *h, *p; |
|
|
|
|
errx(1, "Can't allocate memory."); |
errx(1, "Can't allocate memory."); |
if ((p = strchr(h, '%')) != NULL) |
if ((p = strchr(h, '%')) != NULL) |
*p = '\0'; |
*p = '\0'; |
fprintf(fin, "[%s]", h); |
ftp_printf(fin, ssl, "[%s]", h); |
free(h); |
free(h); |
} else |
} else |
fprintf(fin, "%s", host); |
ftp_printf(fin, ssl, "%s", host); |
|
|
/* |
/* |
* Send port number only if it's specified and does not equal |
* Send port number only if it's specified and does not equal |
* 80. Some broken HTTP servers get confused if you explicitly |
* 80. Some broken HTTP servers get confused if you explicitly |
* send them the port number. |
* 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) |
if (port && strcmp(port, "80") != 0) |
fprintf(fin, ":%s", port); |
ftp_printf(fin, ssl, ":%s", port); |
fprintf(fin, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); |
#endif |
|
ftp_printf(fin, ssl, "\r\n%s\r\n\r\n", HTTP_USER_AGENT); |
if (verbose) |
if (verbose) |
fprintf(ttyout, "\n"); |
fprintf(ttyout, "\n"); |
} |
} |
if (fflush(fin) == EOF) { |
if (fin != NULL && fflush(fin) == EOF) { |
warn("Writing HTTP request"); |
warn("Writing HTTP request"); |
goto cleanup_url_get; |
goto cleanup_url_get; |
} |
} |
|
if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { |
if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { |
|
warn("Receiving HTTP reply"); |
warn("Receiving HTTP reply"); |
goto cleanup_url_get; |
goto cleanup_url_get; |
} |
} |
|
|
filesize = -1; |
filesize = -1; |
|
|
while (1) { |
while (1) { |
if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { |
if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { |
warn("Receiving HTTP reply"); |
warn("Receiving HTTP reply"); |
goto cleanup_url_get; |
goto cleanup_url_get; |
} |
} |
|
|
while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) |
while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) |
buf[--len] = '\0'; |
buf[--len] = '\0'; |
if (len == 0) |
if (len == 0) |
|
|
if ((buf = malloc(4096)) == NULL) |
if ((buf = malloc(4096)) == NULL) |
errx(1, "Can't allocate memory for transfer buffer"); |
errx(1, "Can't allocate memory for transfer buffer"); |
i = 0; |
i = 0; |
while ((len = fread(buf, sizeof(char), 4096, fin)) > 0) { |
len = 1; |
|
while (len > 0) { |
|
len = ftp_read(fin, ssl, buf, 4096); |
bytes += len; |
bytes += len; |
for (cp = buf; len > 0; len -= i, cp += i) { |
for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { |
if ((i = write(out, cp, len)) == -1) { |
if ((i = write(out, cp, wlen)) == -1) { |
warn("Writing %s", savefile); |
warn("Writing %s", savefile); |
goto cleanup_url_get; |
goto cleanup_url_get; |
} |
} |
|
|
warnx("Improper response from %s", host); |
warnx("Improper response from %s", host); |
|
|
cleanup_url_get: |
cleanup_url_get: |
|
#ifndef SMALL |
|
if (ssl) { |
|
SSL_shutdown(ssl); |
|
SSL_free(ssl); |
|
} |
|
#endif |
if (fin != NULL) |
if (fin != NULL) |
fclose(fin); |
fclose(fin); |
else if (s != -1) |
else if (s != -1) |
|
|
* Try HTTP URL-style arguments first. |
* Try HTTP URL-style arguments first. |
*/ |
*/ |
if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || |
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) { |
strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) { |
redirect_loop = 0; |
redirect_loop = 0; |
if (url_get(line, httpproxy, outfile) == -1) |
if (url_get(line, httpproxy, outfile) == -1) |
|
|
*ret = ' '; |
*ret = ' '; |
continue; |
continue; |
} |
} |
/* Can't use strtol here because next char after %xx may be |
|
* a digit. */ |
/* Cannot use strtol here because next char |
|
* after %xx may be a digit. |
|
*/ |
if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { |
if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { |
*ret = hextochar(&str[i+1]); |
*ret = hextochar(&str[i+1]); |
i+=2; |
i+=2; |
|
|
|
|
if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || |
if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || |
strncasecmp(p, HTTP_URL, sizeof(HTTP_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 || |
strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || |
strstr(p, ":/")) |
strstr(p, ":/")) |
return (1); |
return (1); |
return (0); |
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 |