Annotation of src/usr.bin/ftp/fetch.c, Revision 1.122
1.122 ! guenther 1: /* $OpenBSD: fetch.c,v 1.121 2014/05/19 20:09:22 jca Exp $ */
1.15 millert 2: /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */
1.1 millert 3:
4: /*-
5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Jason Thorpe and Luke Mewburn.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.15 millert 23: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 millert 25: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30: * POSSIBILITY OF SUCH DAMAGE.
31: */
32:
33: /*
34: * FTP User Program -- Command line file retrieval
35: */
36:
37: #include <sys/types.h>
38: #include <sys/param.h>
39: #include <sys/socket.h>
1.22 deraadt 40: #include <sys/stat.h>
1.1 millert 41:
42: #include <netinet/in.h>
43:
44: #include <arpa/ftp.h>
45: #include <arpa/inet.h>
46:
47: #include <ctype.h>
48: #include <err.h>
1.17 millert 49: #include <libgen.h>
1.58 grunk 50: #include <limits.h>
1.1 millert 51: #include <netdb.h>
52: #include <fcntl.h>
1.3 millert 53: #include <signal.h>
1.1 millert 54: #include <stdio.h>
1.61 deraadt 55: #include <stdarg.h>
1.19 deraadt 56: #include <errno.h>
1.1 millert 57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
1.40 fgsch 60: #include <util.h>
1.73 drahn 61: #include <resolv.h>
1.1 millert 62:
1.61 deraadt 63: #ifndef SMALL
64: #include <openssl/ssl.h>
65: #include <openssl/err.h>
1.118 jca 66: #include <openssl/x509v3.h>
1.78 martynas 67: #else /* !SMALL */
1.61 deraadt 68: #define SSL void
1.78 martynas 69: #endif /* !SMALL */
1.61 deraadt 70:
1.1 millert 71: #include "ftp_var.h"
1.86 martynas 72: #include "cmds.h"
1.1 millert 73:
1.38 millert 74: static int url_get(const char *, const char *, const char *);
75: void aborthttp(int);
76: void abortfile(int);
1.42 deraadt 77: char hextochar(const char *);
78: char *urldecode(const char *);
1.61 deraadt 79: int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4)));
80: char *ftp_readline(FILE *, SSL *, size_t *);
1.65 ray 81: size_t ftp_read(FILE *, SSL *, char *, size_t);
1.61 deraadt 82: #ifndef SMALL
1.81 espie 83: int proxy_connect(int, char *, char *);
1.61 deraadt 84: int SSL_vprintf(SSL *, const char *, va_list);
85: char *SSL_readline(SSL *, size_t *);
1.118 jca 86: int ssl_match_hostname(char *, char *);
87: int ssl_check_subject_altname(X509 *, char *);
88: int ssl_check_common_name(X509 *, char *);
89: int ssl_check_hostname(X509 *, char *);
1.119 jca 90: SSL_CTX *ssl_get_ssl_ctx(void);
1.78 martynas 91: #endif /* !SMALL */
1.14 millert 92:
1.1 millert 93: #define FTP_URL "ftp://" /* ftp URL prefix */
94: #define HTTP_URL "http://" /* http URL prefix */
1.61 deraadt 95: #define HTTPS_URL "https://" /* https URL prefix */
1.22 deraadt 96: #define FILE_URL "file:" /* file URL prefix */
1.6 millert 97: #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
1.1 millert 98: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
99:
1.73 drahn 100: #define COOKIE_MAX_LEN 42
1.1 millert 101:
102: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
103:
1.110 guenther 104: static const char at_encoding_warning[] =
1.53 deraadt 105: "Extra `@' characters in usernames and passwords should be encoded as %%40";
1.37 heko 106:
1.1 millert 107: jmp_buf httpabort;
108:
1.54 fgsch 109: static int redirect_loop;
110:
1.1 millert 111: /*
1.95 martynas 112: * Determine whether the character needs encoding, per RFC1738:
113: * - No corresponding graphic US-ASCII.
114: * - Unsafe characters.
115: */
116: static int
117: unsafe_char(const char *c)
118: {
119: const char *unsafe_chars = " <>\"#{}|\\^~[]`";
120:
121: /*
122: * No corresponding graphic US-ASCII.
123: * Control characters and octets not used in US-ASCII.
124: */
125: return (iscntrl(*c) || !isascii(*c) ||
126:
127: /*
128: * Unsafe characters.
129: * '%' is also unsafe, if is not followed by two
130: * hexadecimal digits.
131: */
132: strchr(unsafe_chars, *c) != NULL ||
133: (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c))));
134: }
135:
136: /*
137: * Encode given URL, per RFC1738.
138: * Allocate and return string to the caller.
139: */
140: static char *
141: url_encode(const char *path)
142: {
143: size_t i, length, new_length;
144: char *epath, *epathp;
145:
146: length = new_length = strlen(path);
147:
148: /*
149: * First pass:
150: * Count unsafe characters, and determine length of the
151: * final URL.
152: */
153: for (i = 0; i < length; i++)
154: if (unsafe_char(path + i))
155: new_length += 2;
156:
157: epath = epathp = malloc(new_length + 1); /* One more for '\0'. */
158: if (epath == NULL)
1.102 halex 159: err(1, "Can't allocate memory for URL encoding");
1.95 martynas 160:
161: /*
162: * Second pass:
163: * Encode, and copy final URL.
164: */
165: for (i = 0; i < length; i++)
166: if (unsafe_char(path + i)) {
167: snprintf(epathp, 4, "%%" "%02x", path[i]);
168: epathp += 3;
169: } else
170: *(epathp++) = path[i];
171:
172: *epathp = '\0';
173: return (epath);
174: }
175:
1.118 jca 176: #ifndef SMALL
177: int
178: ssl_match_hostname(char *cert_hostname, char *hostname)
179: {
180: if (strcasecmp(cert_hostname, hostname) == 0)
181: return 0;
182:
183: /* wildcard match? */
184: if (cert_hostname[0] == '*') {
185: char *cert_domain, *domain;
186:
187: cert_domain = &cert_hostname[1];
188: if (cert_domain[0] != '.')
189: return -1;
190: if (strlen(cert_domain) == 1)
191: return -1;
192:
193: domain = strchr(hostname, '.');
194: /* no wildcard match against a hostname with no domain part */
195: if (domain == NULL || strlen(domain) == 1)
196: return -1;
197:
198: if (strcasecmp(cert_domain, domain) == 0)
199: return 0;
200: }
201:
202: return -1;
203: }
204:
205: int
206: ssl_check_subject_altname(X509 *cert, char *host)
207: {
208: STACK_OF(GENERAL_NAME) *altname_stack = NULL;
209: union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
210: int addrlen, type;
211: int count, i;
212: int rv = -1;
213:
214: altname_stack =
215: X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
216: if (altname_stack == NULL)
217: return -1;
218:
219: if (inet_pton(AF_INET, host, &addrbuf) == 1) {
220: type = GEN_IPADD;
221: addrlen = 4;
222: } else if (inet_pton(AF_INET6, host, &addrbuf) == 1) {
223: type = GEN_IPADD;
224: addrlen = 16;
225: } else
226: type = GEN_DNS;
227:
228: count = sk_GENERAL_NAME_num(altname_stack);
229: for (i = 0; i < count; i++) {
230: GENERAL_NAME *altname;
231:
232: altname = sk_GENERAL_NAME_value(altname_stack, i);
233:
234: if (altname->type != type)
235: continue;
236:
237: if (type == GEN_DNS) {
238: unsigned char *data;
239: int format;
240:
241: format = ASN1_STRING_type(altname->d.dNSName);
242: if (format == V_ASN1_IA5STRING) {
243: data = ASN1_STRING_data(altname->d.dNSName);
244:
245: if (ASN1_STRING_length(altname->d.dNSName) !=
246: (int)strlen(data)) {
247: fprintf(ttyout, "%s: NUL byte in "
248: "subjectAltName, probably a "
249: "malicious certificate.\n",
250: getprogname());
251: rv = -2;
252: break;
253: }
254:
255: if (ssl_match_hostname(data, host) == 0) {
256: rv = 0;
257: break;
258: }
259: } else
260: fprintf(ttyout, "%s: unhandled subjectAltName "
261: "dNSName encoding (%d)\n", getprogname(),
262: format);
263:
264: } else if (type == GEN_IPADD) {
265: unsigned char *data;
266: int datalen;
267:
268: datalen = ASN1_STRING_length(altname->d.iPAddress);
269: data = ASN1_STRING_data(altname->d.iPAddress);
270:
271: if (datalen == addrlen &&
272: memcmp(data, &addrbuf, addrlen) == 0) {
273: rv = 0;
274: break;
275: }
276: }
277: }
278:
279: sk_GENERAL_NAME_free(altname_stack);
280: return rv;
281: }
282:
283: int
284: ssl_check_common_name(X509 *cert, char *host)
285: {
286: X509_NAME *name;
287: char *common_name = NULL;
288: int common_name_len;
289: int rv = -1;
290:
291: name = X509_get_subject_name(cert);
292: if (name == NULL)
293: goto out;
294:
295: common_name_len = X509_NAME_get_text_by_NID(name, NID_commonName,
296: NULL, 0);
297: if (common_name_len < 0)
298: goto out;
299:
300: common_name = calloc(common_name_len + 1, 1);
301: if (common_name == NULL)
302: goto out;
303:
304: X509_NAME_get_text_by_NID(name, NID_commonName, common_name,
305: common_name_len + 1);
306:
307: /* NUL bytes in CN? */
308: if (common_name_len != (int)strlen(common_name)) {
309: fprintf(ttyout, "%s: NUL byte in Common Name field, "
310: "probably a malicious certificate.\n", getprogname());
311: rv = -2;
312: goto out;
313: }
314:
315: if (ssl_match_hostname(common_name, host) == 0)
316: rv = 0;
317: out:
318: free(common_name);
319: return rv;
320: }
321:
322: int
323: ssl_check_hostname(X509 *cert, char *host)
324: {
325: int rv;
326:
327: rv = ssl_check_subject_altname(cert, host);
328: if (rv == 0 || rv == -2)
329: return rv;
330:
331: return ssl_check_common_name(cert, host);
332: }
1.119 jca 333:
334: SSL_CTX *
335: ssl_get_ssl_ctx(void)
336: {
1.120 jca 337: static SSL_CTX *ssl_ctx = NULL;
338: static int libssl_loaded = 0;
1.119 jca 339:
340: if (ssl_ctx != NULL)
341: return ssl_ctx;
342:
343: if (!libssl_loaded) {
344: SSL_library_init();
345: SSL_load_error_strings();
346: libssl_loaded = 1;
347: }
348:
349: ssl_ctx = SSL_CTX_new(SSLv23_client_method());
350: if (ssl_ctx == NULL)
351: goto err;
352:
353: if (ssl_verify) {
354: if (ssl_ca_file == NULL && ssl_ca_path == NULL)
355: ssl_ca_file = _PATH_SSL_CAFILE;
356:
357: if (SSL_CTX_load_verify_locations(ssl_ctx,
358: ssl_ca_file, ssl_ca_path) != 1)
359: goto err;
360:
361: SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
362: if (ssl_verify_depth != -1)
363: SSL_CTX_set_verify_depth(ssl_ctx,
364: ssl_verify_depth);
365: }
366:
367: if (ssl_ciphers != NULL &&
368: SSL_CTX_set_cipher_list(ssl_ctx, ssl_ciphers) == -1)
369: goto err;
370:
371: return ssl_ctx;
372: err:
373: if (ssl_ctx != NULL) {
374: SSL_CTX_free(ssl_ctx);
375: ssl_ctx = NULL;
376: }
377: return NULL;
378: }
1.118 jca 379: #endif
380:
1.95 martynas 381: /*
1.6 millert 382: * Retrieve URL, via the proxy in $proxyvar if necessary.
1.1 millert 383: * Modifies the string argument given.
384: * Returns -1 on failure, 0 on success
385: */
1.14 millert 386: static int
1.50 deraadt 387: url_get(const char *origline, const char *proxyenv, const char *outfile)
1.1 millert 388: {
1.69 jsg 389: char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4];
1.62 ray 390: char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
1.106 haesbaer 391: char *epath, *redirurl, *loctail, *h, *p;
1.53 deraadt 392: int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
1.105 haesbaer 393: struct addrinfo hints, *res0, *res, *ares = NULL;
1.34 millert 394: const char * volatile savefile;
1.62 ray 395: char * volatile proxyurl = NULL;
1.73 drahn 396: char *cookie = NULL;
1.53 deraadt 397: volatile int s = -1, out;
1.97 martynas 398: volatile sig_t oldintr, oldinti;
1.53 deraadt 399: FILE *fin = NULL;
1.1 millert 400: off_t hashbytes;
1.58 grunk 401: const char *errstr;
1.114 tedu 402: ssize_t len, wlen;
1.61 deraadt 403: #ifndef SMALL
404: char *sslpath = NULL, *sslhost = NULL;
1.106 haesbaer 405: char *locbase, *full_host = NULL, *auth = NULL;
1.102 halex 406: const char *scheme;
1.70 deraadt 407: int ishttpsurl = 0;
1.61 deraadt 408: SSL_CTX *ssl_ctx = NULL;
1.78 martynas 409: #endif /* !SMALL */
1.61 deraadt 410: SSL *ssl = NULL;
1.70 deraadt 411: int status;
1.105 haesbaer 412: int save_errno;
1.113 tedu 413: const size_t buflen = 128 * 1024;
1.14 millert 414:
1.97 martynas 415: direction = "received";
416:
1.62 ray 417: newline = strdup(origline);
418: if (newline == NULL)
1.14 millert 419: errx(1, "Can't allocate memory to parse URL");
1.102 halex 420: if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
1.62 ray 421: host = newline + sizeof(HTTP_URL) - 1;
1.102 halex 422: #ifndef SMALL
423: scheme = HTTP_URL;
424: #endif /* !SMALL */
425: } else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.62 ray 426: host = newline + sizeof(FTP_URL) - 1;
1.14 millert 427: isftpurl = 1;
1.102 halex 428: #ifndef SMALL
429: scheme = FTP_URL;
430: #endif /* !SMALL */
1.62 ray 431: } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
432: host = newline + sizeof(FILE_URL) - 1;
1.22 deraadt 433: isfileurl = 1;
1.61 deraadt 434: #ifndef SMALL
1.102 halex 435: scheme = FILE_URL;
1.62 ray 436: } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) {
437: host = newline + sizeof(HTTPS_URL) - 1;
1.61 deraadt 438: ishttpsurl = 1;
1.102 halex 439: scheme = HTTPS_URL;
1.78 martynas 440: #endif /* !SMALL */
1.14 millert 441: } else
1.62 ray 442: errx(1, "url_get: Invalid URL '%s'", newline);
1.6 millert 443:
1.22 deraadt 444: if (isfileurl) {
445: path = host;
446: } else {
1.93 martynas 447: path = strchr(host, '/'); /* Find path */
1.22 deraadt 448: if (EMPTYSTRING(path)) {
1.93 martynas 449: if (outfile) { /* No slash, but */
450: path=strchr(host,'\0'); /* we have outfile. */
451: goto noslash;
452: }
1.22 deraadt 453: if (isftpurl)
454: goto noftpautologin;
1.94 martynas 455: warnx("No `/' after host (use -o): %s", origline);
1.22 deraadt 456: goto cleanup_url_get;
457: }
458: *path++ = '\0';
1.93 martynas 459: if (EMPTYSTRING(path) && !outfile) {
1.22 deraadt 460: if (isftpurl)
461: goto noftpautologin;
1.94 martynas 462: warnx("No filename after host (use -o): %s", origline);
1.22 deraadt 463: goto cleanup_url_get;
464: }
1.14 millert 465: }
1.1 millert 466:
1.93 martynas 467: noslash:
1.106 haesbaer 468:
469: #ifndef SMALL
470: /*
471: * Look for auth header in host, since now host does not
472: * contain the path. Basic auth from RFC 2617, valid
473: * characters for path are in RFC 3986 section 3.3.
474: */
475: if (proxyenv == NULL &&
476: (!strcmp(scheme, HTTP_URL) || !strcmp(scheme, HTTPS_URL))) {
477: if ((p = strchr(host, '@')) != NULL) {
478: size_t authlen = (strlen(host) + 5) * 4 / 3;
479: *p = 0; /* Kill @ */
480: if ((auth = malloc(authlen)) == NULL)
481: err(1, "Can't allocate memory for "
482: "authorization");
483: if (b64_ntop(host, strlen(host),
484: auth, authlen) == -1)
485: errx(1, "error in base64 encoding");
486: host = p + 1;
487: }
488: }
489: #endif /* SMALL */
490:
1.17 millert 491: if (outfile)
492: savefile = outfile;
1.93 martynas 493: else {
494: if (path[strlen(path) - 1] == '/') /* Consider no file */
495: savefile = NULL; /* after dir invalid. */
496: else
497: savefile = basename(path);
1.75 martynas 498: }
499:
1.14 millert 500: if (EMPTYSTRING(savefile)) {
501: if (isftpurl)
502: goto noftpautologin;
1.94 martynas 503: warnx("No filename after directory (use -o): %s", origline);
1.6 millert 504: goto cleanup_url_get;
1.14 millert 505: }
1.1 millert 506:
1.93 martynas 507: #ifndef SMALL
1.100 halex 508: if (resume && pipeout) {
1.93 martynas 509: warnx("can't append to stdout");
510: goto cleanup_url_get;
511: }
512: #endif /* !SMALL */
513:
1.59 uwe 514: if (!isfileurl && proxyenv != NULL) { /* use proxy */
1.61 deraadt 515: #ifndef SMALL
516: if (ishttpsurl) {
517: sslpath = strdup(path);
518: sslhost = strdup(host);
519: if (! sslpath || ! sslhost)
520: errx(1, "Can't allocate memory for https path/host.");
521: }
1.78 martynas 522: #endif /* !SMALL */
1.62 ray 523: proxyurl = strdup(proxyenv);
524: if (proxyurl == NULL)
1.14 millert 525: errx(1, "Can't allocate memory for proxy URL.");
1.62 ray 526: if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
527: host = proxyurl + sizeof(HTTP_URL) - 1;
528: else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0)
529: host = proxyurl + sizeof(FTP_URL) - 1;
1.6 millert 530: else {
1.14 millert 531: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 532: goto cleanup_url_get;
533: }
1.14 millert 534: if (EMPTYSTRING(host)) {
535: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 536: goto cleanup_url_get;
1.14 millert 537: }
1.93 martynas 538: if (*--path == '\0')
539: *path = '/'; /* add / back to real path */
1.1 millert 540: path = strchr(host, '/'); /* remove trailing / on host */
1.42 deraadt 541: if (!EMPTYSTRING(path))
1.73 drahn 542: *path++ = '\0'; /* i guess this ++ is useless */
543:
544: path = strchr(host, '@'); /* look for credentials in proxy */
545: if (!EMPTYSTRING(path)) {
1.81 espie 546: *path = '\0';
1.73 drahn 547: cookie = strchr(host, ':');
548: if (EMPTYSTRING(cookie)) {
549: warnx("Malformed proxy URL: %s", proxyenv);
550: goto cleanup_url_get;
551: }
552: cookie = malloc(COOKIE_MAX_LEN);
1.81 espie 553: if (cookie == NULL)
554: errx(1, "out of memory");
555: if (b64_ntop(host, strlen(host), cookie, COOKIE_MAX_LEN) == -1)
556: errx(1, "error in base64 encoding");
557: *path = '@'; /* restore @ in proxyurl */
1.73 drahn 558: /*
1.81 espie 559: * This removes the password from proxyurl,
1.73 drahn 560: * filling with stars
561: */
1.81 espie 562: for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@';
1.73 drahn 563: host++)
564: *host = '*';
565:
1.81 espie 566: host = path + 1;
1.73 drahn 567: }
1.62 ray 568: path = newline;
1.1 millert 569: }
570:
1.22 deraadt 571: if (isfileurl) {
572: struct stat st;
573:
574: s = open(path, O_RDONLY);
575: if (s == -1) {
576: warn("Can't open file %s", path);
577: goto cleanup_url_get;
578: }
579:
580: if (fstat(s, &st) == -1)
581: filesize = -1;
582: else
583: filesize = st.st_size;
584:
1.83 martynas 585: /* Open the output file. */
1.100 halex 586: if (!pipeout) {
1.75 martynas 587: #ifndef SMALL
588: if (resume)
1.82 deraadt 589: out = open(savefile, O_CREAT | O_WRONLY |
590: O_APPEND, 0666);
591:
1.75 martynas 592: else
1.78 martynas 593: #endif /* !SMALL */
1.75 martynas 594: out = open(savefile, O_CREAT | O_WRONLY |
595: O_TRUNC, 0666);
1.22 deraadt 596: if (out < 0) {
597: warn("Can't open %s", savefile);
598: goto cleanup_url_get;
599: }
600: } else
601: out = fileno(stdout);
602:
1.75 martynas 603: #ifndef SMALL
604: if (resume) {
605: if (fstat(out, &st) == -1) {
606: warn("Can't fstat %s", savefile);
607: goto cleanup_url_get;
608: }
609: if (lseek(s, st.st_size, SEEK_SET) == -1) {
610: warn("Can't lseek %s", path);
611: goto cleanup_url_get;
612: }
613: restart_point = st.st_size;
614: }
1.78 martynas 615: #endif /* !SMALL */
1.75 martynas 616:
1.22 deraadt 617: /* Trap signals */
618: oldintr = NULL;
1.97 martynas 619: oldinti = NULL;
1.22 deraadt 620: if (setjmp(httpabort)) {
621: if (oldintr)
622: (void)signal(SIGINT, oldintr);
1.97 martynas 623: if (oldinti)
624: (void)signal(SIGINFO, oldinti);
1.22 deraadt 625: goto cleanup_url_get;
626: }
627: oldintr = signal(SIGINT, abortfile);
1.42 deraadt 628:
1.22 deraadt 629: bytes = 0;
630: hashbytes = mark;
1.84 martynas 631: progressmeter(-1, path);
1.40 fgsch 632:
1.113 tedu 633: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 634: errx(1, "Can't allocate memory for transfer buffer");
1.42 deraadt 635:
1.22 deraadt 636: /* Finally, suck down the file. */
637: i = 0;
1.97 martynas 638: oldinti = signal(SIGINFO, psummary);
1.113 tedu 639: while ((len = read(s, buf, buflen)) > 0) {
1.22 deraadt 640: bytes += len;
641: for (cp = buf; len > 0; len -= i, cp += i) {
642: if ((i = write(out, cp, len)) == -1) {
643: warn("Writing %s", savefile);
1.97 martynas 644: signal(SIGINFO, oldinti);
1.22 deraadt 645: goto cleanup_url_get;
646: }
647: else if (i == 0)
648: break;
649: }
650: if (hash && !progress) {
651: while (bytes >= hashbytes) {
652: (void)putc('#', ttyout);
653: hashbytes += mark;
654: }
655: (void)fflush(ttyout);
656: }
657: }
1.97 martynas 658: signal(SIGINFO, oldinti);
1.22 deraadt 659: if (hash && !progress && bytes > 0) {
660: if (bytes < mark)
661: (void)putc('#', ttyout);
662: (void)putc('\n', ttyout);
663: (void)fflush(ttyout);
664: }
665: if (len != 0) {
666: warn("Reading from file");
667: goto cleanup_url_get;
668: }
1.84 martynas 669: progressmeter(1, NULL);
1.22 deraadt 670: if (verbose)
1.97 martynas 671: ptransfer(0);
1.22 deraadt 672: (void)signal(SIGINT, oldintr);
1.42 deraadt 673:
1.40 fgsch 674: rval = 0;
675: goto cleanup_url_get;
1.22 deraadt 676: }
677:
1.28 itojun 678: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
679: (hosttail[1] == '\0' || hosttail[1] == ':')) {
680: host++;
681: *hosttail++ = '\0';
1.102 halex 682: #ifndef SMALL
683: if (asprintf(&full_host, "[%s]", host) == -1)
684: errx(1, "Cannot allocate memory for hostname");
685: #endif /* !SMALL */
1.28 itojun 686: } else
687: hosttail = host;
688:
689: portnum = strrchr(hosttail, ':'); /* find portnum */
1.1 millert 690: if (portnum != NULL)
691: *portnum++ = '\0';
692:
1.80 martynas 693: #ifndef SMALL
1.102 halex 694: if (full_host == NULL)
695: if ((full_host = strdup(host)) == NULL)
696: errx(1, "Cannot allocate memory for hostname");
1.1 millert 697: if (debug)
1.106 haesbaer 698: fprintf(ttyout, "host %s, port %s, path %s, "
699: "save as %s, auth %s.\n",
700: host, portnum, path, savefile, auth);
1.80 martynas 701: #endif /* !SMALL */
1.1 millert 702:
1.25 itojun 703: memset(&hints, 0, sizeof(hints));
1.39 deraadt 704: hints.ai_family = family;
1.25 itojun 705: hints.ai_socktype = SOCK_STREAM;
1.61 deraadt 706: #ifndef SMALL
707: port = portnum ? portnum : (ishttpsurl ? httpsport : httpport);
1.78 martynas 708: #else /* !SMALL */
1.25 itojun 709: port = portnum ? portnum : httpport;
1.78 martynas 710: #endif /* !SMALL */
1.25 itojun 711: error = getaddrinfo(host, port, &hints, &res0);
1.61 deraadt 712: /*
713: * If the services file is corrupt/missing, fall back
714: * on our hard-coded defines.
715: */
1.30 deraadt 716: if (error == EAI_SERVICE && port == httpport) {
717: snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT);
718: error = getaddrinfo(host, pbuf, &hints, &res0);
1.61 deraadt 719: #ifndef SMALL
720: } else if (error == EAI_SERVICE && port == httpsport) {
721: snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT);
722: error = getaddrinfo(host, pbuf, &hints, &res0);
1.78 martynas 723: #endif /* !SMALL */
1.30 deraadt 724: }
1.25 itojun 725: if (error) {
1.122 ! guenther 726: warnx("%s: %s", host, gai_strerror(error));
1.25 itojun 727: goto cleanup_url_get;
1.1 millert 728: }
729:
1.105 haesbaer 730: #ifndef SMALL
731: if (srcaddr) {
732: hints.ai_flags |= AI_NUMERICHOST;
733: error = getaddrinfo(srcaddr, NULL, &hints, &ares);
734: if (error) {
1.122 ! guenther 735: warnx("%s: %s", srcaddr, gai_strerror(error));
1.105 haesbaer 736: goto cleanup_url_get;
737: }
738: }
739: #endif /* !SMALL */
740:
1.25 itojun 741: s = -1;
742: for (res = res0; res; res = res->ai_next) {
1.44 itojun 743: if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
744: sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
745: strlcpy(hbuf, "(unknown)", sizeof(hbuf));
1.41 deraadt 746: if (verbose)
1.44 itojun 747: fprintf(ttyout, "Trying %s...\n", hbuf);
1.14 millert 748:
1.25 itojun 749: s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
750: if (s == -1) {
751: cause = "socket";
752: continue;
1.1 millert 753: }
754:
1.105 haesbaer 755: #ifndef SMALL
756: if (srcaddr) {
757: if (ares->ai_family != res->ai_family) {
758: close(s);
759: s = -1;
760: errno = EINVAL;
761: cause = "bind";
762: continue;
763: }
764: if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) {
765: save_errno = errno;
766: close(s);
767: errno = save_errno;
768: s = -1;
769: cause = "bind";
770: continue;
771: }
772: }
773: #endif /* !SMALL */
774:
1.25 itojun 775: again:
776: if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
777: if (errno == EINTR)
778: goto again;
1.57 otto 779: save_errno = errno;
1.25 itojun 780: close(s);
1.57 otto 781: errno = save_errno;
1.25 itojun 782: s = -1;
783: cause = "connect";
1.19 deraadt 784: continue;
785: }
1.25 itojun 786:
1.29 itojun 787: /* get port in numeric */
788: if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0,
789: pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0)
790: port = pbuf;
791: else
792: port = NULL;
793:
1.61 deraadt 794: #ifndef SMALL
795: if (proxyenv && sslhost)
1.81 espie 796: proxy_connect(s, sslhost, cookie);
1.78 martynas 797: #endif /* !SMALL */
1.25 itojun 798: break;
799: }
800: freeaddrinfo(res0);
1.105 haesbaer 801: #ifndef SMALL
802: if (srcaddr)
803: freeaddrinfo(ares);
804: #endif /* !SMALL */
1.25 itojun 805: if (s < 0) {
1.33 millert 806: warn("%s", cause);
1.6 millert 807: goto cleanup_url_get;
1.1 millert 808: }
809:
1.61 deraadt 810: #ifndef SMALL
811: if (ishttpsurl) {
1.117 jca 812: union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
813:
1.61 deraadt 814: if (proxyenv && sslpath) {
815: ishttpsurl = 0;
1.62 ray 816: proxyurl = NULL;
1.61 deraadt 817: path = sslpath;
818: }
1.119 jca 819: ssl_ctx = ssl_get_ssl_ctx();
1.112 jca 820: if (ssl_ctx == NULL) {
1.116 jca 821: ERR_print_errors_fp(ttyout);
822: goto cleanup_url_get;
823: }
1.61 deraadt 824: ssl = SSL_new(ssl_ctx);
1.112 jca 825: if (ssl == NULL) {
1.61 deraadt 826: ERR_print_errors_fp(ttyout);
827: goto cleanup_url_get;
828: }
829: if (SSL_set_fd(ssl, s) == 0) {
830: ERR_print_errors_fp(ttyout);
831: goto cleanup_url_get;
1.117 jca 832: }
833: /*
834: * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
835: * permitted in "HostName".
836: */
837: if (inet_pton(AF_INET, host, &addrbuf) != 1 &&
838: inet_pton(AF_INET6, host, &addrbuf) != 1) {
839: if (SSL_set_tlsext_host_name(ssl, host) == 0) {
840: ERR_print_errors_fp(ttyout);
841: goto cleanup_url_get;
842: }
1.61 deraadt 843: }
844: if (SSL_connect(ssl) <= 0) {
845: ERR_print_errors_fp(ttyout);
1.72 ray 846: goto cleanup_url_get;
1.118 jca 847: }
848: if (ssl_verify) {
849: X509 *cert;
850:
851: cert = SSL_get_peer_certificate(ssl);
852: if (cert == NULL) {
853: fprintf(ttyout, "%s: no server certificate\n",
854: getprogname());
855: goto cleanup_url_get;
856: }
857:
858: if (ssl_check_hostname(cert, host) != 0) {
1.121 jca 859: X509_free(cert);
1.118 jca 860: fprintf(ttyout, "%s: host `%s' not present in"
861: " server certificate\n",
862: getprogname(), host);
863: goto cleanup_url_get;
864: }
865:
866: X509_free(cert);
1.61 deraadt 867: }
868: } else {
869: fin = fdopen(s, "r+");
870: }
1.78 martynas 871: #else /* !SMALL */
1.40 fgsch 872: fin = fdopen(s, "r+");
1.78 martynas 873: #endif /* !SMALL */
1.40 fgsch 874:
1.55 fgsch 875: if (verbose)
876: fprintf(ttyout, "Requesting %s", origline);
1.95 martynas 877:
1.1 millert 878: /*
1.40 fgsch 879: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 880: */
1.74 pyr 881: #ifndef SMALL
882: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 883: #endif /* !SMALL */
1.95 martynas 884:
885: epath = url_encode(path);
1.62 ray 886: if (proxyurl) {
1.55 fgsch 887: if (verbose)
1.81 espie 888: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 889: /*
890: * Host: directive must use the destination host address for
891: * the original URI (path). We do not attach it at this moment.
892: */
1.73 drahn 893: if (cookie)
894: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 895: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
1.95 martynas 896: epath, cookie, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 897: else
1.74 pyr 898: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
1.95 martynas 899: epath, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 900:
1.28 itojun 901: } else {
1.90 martynas 902: #ifndef SMALL
903: if (resume) {
904: struct stat stbuf;
905:
906: if (stat(savefile, &stbuf) == 0)
907: restart_point = stbuf.st_size;
908: else
909: restart_point = 0;
910: }
1.106 haesbaer 911: if (auth) {
912: ftp_printf(fin, ssl,
913: "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ",
914: epath, restart_point ?
915: "HTTP/1.1\r\nConnection: close" : "HTTP/1.0",
916: auth);
917: free(auth);
918: auth = NULL;
919: } else
920: #endif /* SMALL */
1.107 haesbaer 921: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
922: #ifndef SMALL
923: restart_point ? "HTTP/1.1\r\nConnection: close" :
924: #endif /* !SMALL */
925: "HTTP/1.0");
1.32 itojun 926: if (strchr(host, ':')) {
1.55 fgsch 927: /*
928: * strip off scoped address portion, since it's
929: * local to node
930: */
1.32 itojun 931: h = strdup(host);
932: if (h == NULL)
933: errx(1, "Can't allocate memory.");
934: if ((p = strchr(h, '%')) != NULL)
935: *p = '\0';
1.61 deraadt 936: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 937: free(h);
1.55 fgsch 938: } else
1.61 deraadt 939: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 940:
941: /*
942: * Send port number only if it's specified and does not equal
943: * 80. Some broken HTTP servers get confused if you explicitly
944: * send them the port number.
945: */
1.61 deraadt 946: #ifndef SMALL
947: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
948: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 949: if (restart_point)
1.75 martynas 950: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
951: (long long)restart_point);
1.78 martynas 952: #else /* !SMALL */
1.55 fgsch 953: if (port && strcmp(port, "80") != 0)
1.61 deraadt 954: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 955: #endif /* !SMALL */
1.74 pyr 956: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
957: buf ? buf : "", HTTP_USER_AGENT);
1.55 fgsch 958: if (verbose)
959: fprintf(ttyout, "\n");
1.28 itojun 960: }
1.95 martynas 961: free(epath);
1.74 pyr 962:
963: #ifndef SMALL
964: free(buf);
1.78 martynas 965: #endif /* !SMALL */
1.74 pyr 966: buf = NULL;
967:
1.61 deraadt 968: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 969: warn("Writing HTTP request");
1.6 millert 970: goto cleanup_url_get;
1.1 millert 971: }
1.61 deraadt 972: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 973: warn("Receiving HTTP reply");
974: goto cleanup_url_get;
1.1 millert 975: }
1.40 fgsch 976:
977: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
978: buf[--len] = '\0';
1.80 martynas 979: #ifndef SMALL
1.40 fgsch 980: if (debug)
981: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 982: #endif /* !SMALL */
1.40 fgsch 983:
1.1 millert 984: cp = strchr(buf, ' ');
985: if (cp == NULL)
986: goto improper;
987: else
988: cp++;
1.69 jsg 989:
990: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 991: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 992: if (errstr) {
993: warnx("Error retrieving file: %s", cp);
994: goto cleanup_url_get;
995: }
996:
997: switch (status) {
998: case 200: /* OK */
1.75 martynas 999: #ifndef SMALL
1.109 sthen 1000: /*
1.98 phessler 1001: * When we request a partial file, and we receive an HTTP 200
1002: * it is a good indication that the server doesn't support
1003: * range requests, and is about to send us the entire file.
1004: * If the restart_point == 0, then we are not actually
1005: * requesting a partial file, and an HTTP 200 is appropriate.
1006: */
1007: if (resume && restart_point != 0) {
1008: warnx("Server does not support resume.");
1009: restart_point = resume = 0;
1010: }
1.101 halex 1011: /* FALLTHROUGH */
1.75 martynas 1012: case 206: /* Partial Content */
1.91 halex 1013: #endif /* !SMALL */
1.69 jsg 1014: break;
1015: case 301: /* Moved Permanently */
1016: case 302: /* Found */
1017: case 303: /* See Other */
1018: case 307: /* Temporary Redirect */
1.40 fgsch 1019: isredirect++;
1.54 fgsch 1020: if (redirect_loop++ > 10) {
1021: warnx("Too many redirections requested");
1022: goto cleanup_url_get;
1023: }
1.69 jsg 1024: break;
1.75 martynas 1025: #ifndef SMALL
1026: case 416: /* Requested Range Not Satisfiable */
1027: warnx("File is already fully retrieved.");
1028: goto cleanup_url_get;
1.78 martynas 1029: #endif /* !SMALL */
1.69 jsg 1030: default:
1.1 millert 1031: warnx("Error retrieving file: %s", cp);
1.6 millert 1032: goto cleanup_url_get;
1.1 millert 1033: }
1034:
1035: /*
1036: * Read the rest of the header.
1037: */
1.40 fgsch 1038: free(buf);
1039: filesize = -1;
1040:
1.62 ray 1041: for (;;) {
1.61 deraadt 1042: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 1043: warn("Receiving HTTP reply");
1044: goto cleanup_url_get;
1045: }
1.61 deraadt 1046:
1.40 fgsch 1047: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
1048: buf[--len] = '\0';
1049: if (len == 0)
1.1 millert 1050: break;
1.80 martynas 1051: #ifndef SMALL
1.40 fgsch 1052: if (debug)
1053: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 1054: #endif /* !SMALL */
1.1 millert 1055:
1.40 fgsch 1056: /* Look for some headers */
1057: cp = buf;
1.1 millert 1058: #define CONTENTLEN "Content-Length: "
1.40 fgsch 1059: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
1.104 sthen 1060: size_t s;
1.40 fgsch 1061: cp += sizeof(CONTENTLEN) - 1;
1.111 deraadt 1062: if ((s = strcspn(cp, " \t")))
1.104 sthen 1063: *(cp+s) = 0;
1.58 grunk 1064: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
1065: if (errstr != NULL)
1.40 fgsch 1066: goto improper;
1.75 martynas 1067: #ifndef SMALL
1.90 martynas 1068: if (restart_point)
1.75 martynas 1069: filesize += restart_point;
1.78 martynas 1070: #endif /* !SMALL */
1.40 fgsch 1071: #define LOCATION "Location: "
1072: } else if (isredirect &&
1073: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
1074: cp += sizeof(LOCATION) - 1;
1.102 halex 1075: if (strstr(cp, "://") == NULL) {
1076: #ifdef SMALL
1077: errx(1, "Relative redirect not supported");
1078: #else /* SMALL */
1079: if (*cp == '/') {
1080: locbase = NULL;
1081: cp++;
1082: } else {
1083: locbase = strdup(path);
1084: if (locbase == NULL)
1085: errx(1, "Can't allocate memory"
1086: " for location base");
1087: loctail = strchr(locbase, '#');
1088: if (loctail != NULL)
1089: *loctail = '\0';
1090: loctail = strchr(locbase, '?');
1091: if (loctail != NULL)
1092: *loctail = '\0';
1093: loctail = strrchr(locbase, '/');
1094: if (loctail == NULL) {
1095: free(locbase);
1096: locbase = NULL;
1097: } else
1098: loctail[1] = '\0';
1099: }
1100: /* Contruct URL from relative redirect */
1101: if (asprintf(&redirurl, "%s%s%s%s/%s%s",
1102: scheme, full_host,
1103: portnum ? ":" : "",
1104: portnum ? portnum : "",
1105: locbase ? locbase : "",
1106: cp) == -1)
1107: errx(1, "Cannot build "
1108: "redirect URL");
1109: free(locbase);
1110: #endif /* SMALL */
1111: } else if ((redirurl = strdup(cp)) == NULL)
1112: errx(1, "Cannot allocate memory for URL");
1113: loctail = strchr(redirurl, '#');
1114: if (loctail != NULL)
1115: *loctail = '\0';
1.40 fgsch 1116: if (verbose)
1.102 halex 1117: fprintf(ttyout, "Redirected to %s\n", redirurl);
1.40 fgsch 1118: if (fin != NULL)
1119: fclose(fin);
1120: else if (s != -1)
1121: close(s);
1.102 halex 1122: rval = url_get(redirurl, proxyenv, savefile);
1123: free(redirurl);
1124: goto cleanup_url_get;
1.40 fgsch 1125: }
1.108 tobias 1126: free(buf);
1.1 millert 1127: }
1128:
1.17 millert 1129: /* Open the output file. */
1.100 halex 1130: if (!pipeout) {
1.75 martynas 1131: #ifndef SMALL
1132: if (resume)
1.83 martynas 1133: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
1134: 0666);
1.75 martynas 1135: else
1.78 martynas 1136: #endif /* !SMALL */
1.75 martynas 1137: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
1138: 0666);
1.10 deraadt 1139: if (out < 0) {
1140: warn("Can't open %s", savefile);
1141: goto cleanup_url_get;
1142: }
1143: } else
1.17 millert 1144: out = fileno(stdout);
1.1 millert 1145:
1146: /* Trap signals */
1147: oldintr = NULL;
1.97 martynas 1148: oldinti = NULL;
1.1 millert 1149: if (setjmp(httpabort)) {
1150: if (oldintr)
1.2 millert 1151: (void)signal(SIGINT, oldintr);
1.97 martynas 1152: if (oldinti)
1153: (void)signal(SIGINFO, oldinti);
1.6 millert 1154: goto cleanup_url_get;
1.1 millert 1155: }
1156: oldintr = signal(SIGINT, aborthttp);
1157:
1158: bytes = 0;
1159: hashbytes = mark;
1.84 martynas 1160: progressmeter(-1, path);
1.43 millert 1161:
1162: free(buf);
1.1 millert 1163:
1164: /* Finally, suck down the file. */
1.113 tedu 1165: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 1166: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 1167: i = 0;
1.61 deraadt 1168: len = 1;
1.97 martynas 1169: oldinti = signal(SIGINFO, psummary);
1.61 deraadt 1170: while (len > 0) {
1.113 tedu 1171: len = ftp_read(fin, ssl, buf, buflen);
1.1 millert 1172: bytes += len;
1.61 deraadt 1173: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
1174: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 1175: warn("Writing %s", savefile);
1.97 martynas 1176: signal(SIGINFO, oldinti);
1.6 millert 1177: goto cleanup_url_get;
1.1 millert 1178: }
1179: else if (i == 0)
1180: break;
1181: }
1182: if (hash && !progress) {
1183: while (bytes >= hashbytes) {
1.10 deraadt 1184: (void)putc('#', ttyout);
1.1 millert 1185: hashbytes += mark;
1186: }
1.10 deraadt 1187: (void)fflush(ttyout);
1.1 millert 1188: }
1189: }
1.97 martynas 1190: signal(SIGINFO, oldinti);
1.1 millert 1191: if (hash && !progress && bytes > 0) {
1192: if (bytes < mark)
1.10 deraadt 1193: (void)putc('#', ttyout);
1194: (void)putc('\n', ttyout);
1195: (void)fflush(ttyout);
1.1 millert 1196: }
1197: if (len != 0) {
1198: warn("Reading from socket");
1.6 millert 1199: goto cleanup_url_get;
1.1 millert 1200: }
1.84 martynas 1201: progressmeter(1, NULL);
1.75 martynas 1202: if (
1203: #ifndef SMALL
1204: !resume &&
1.78 martynas 1205: #endif /* !SMALL */
1.75 martynas 1206: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 1207: if (verbose)
1208: fputs("Read short file.\n", ttyout);
1209: goto cleanup_url_get;
1210: }
1211:
1.1 millert 1212: if (verbose)
1.97 martynas 1213: ptransfer(0);
1.2 millert 1214: (void)signal(SIGINT, oldintr);
1.1 millert 1215:
1.40 fgsch 1216: rval = 0;
1217: goto cleanup_url_get;
1.1 millert 1218:
1.14 millert 1219: noftpautologin:
1220: warnx(
1221: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
1222: goto cleanup_url_get;
1223:
1.1 millert 1224: improper:
1.8 millert 1225: warnx("Improper response from %s", host);
1.14 millert 1226:
1.6 millert 1227: cleanup_url_get:
1.61 deraadt 1228: #ifndef SMALL
1229: if (ssl) {
1230: SSL_shutdown(ssl);
1231: SSL_free(ssl);
1232: }
1.102 halex 1233: free(full_host);
1.106 haesbaer 1234: free(auth);
1.78 martynas 1235: #endif /* !SMALL */
1.40 fgsch 1236: if (fin != NULL)
1237: fclose(fin);
1238: else if (s != -1)
1.1 millert 1239: close(s);
1.67 steven 1240: free(buf);
1241: free(proxyurl);
1.62 ray 1242: free(newline);
1.81 espie 1243: free(cookie);
1.40 fgsch 1244: return (rval);
1.1 millert 1245: }
1246:
1247: /*
1248: * Abort a http retrieval
1249: */
1.51 deraadt 1250: /* ARGSUSED */
1.1 millert 1251: void
1.51 deraadt 1252: aborthttp(int signo)
1.1 millert 1253: {
1254:
1255: alarmtimer(0);
1.10 deraadt 1256: fputs("\nhttp fetch aborted.\n", ttyout);
1257: (void)fflush(ttyout);
1.1 millert 1258: longjmp(httpabort, 1);
1259: }
1260:
1261: /*
1.22 deraadt 1262: * Abort a http retrieval
1263: */
1.51 deraadt 1264: /* ARGSUSED */
1.22 deraadt 1265: void
1.51 deraadt 1266: abortfile(int signo)
1.22 deraadt 1267: {
1268:
1269: alarmtimer(0);
1270: fputs("\nfile fetch aborted.\n", ttyout);
1271: (void)fflush(ttyout);
1272: longjmp(httpabort, 1);
1273: }
1274:
1275: /*
1.1 millert 1276: * Retrieve multiple files from the command line, transferring
1277: * files of the form "host:path", "ftp://host/path" using the
1278: * ftp protocol, and files of the form "http://host/path" using
1279: * the http protocol.
1.2 millert 1280: * If path has a trailing "/", then return (-1);
1.1 millert 1281: * the path will be cd-ed into and the connection remains open,
1282: * and the function will return -1 (to indicate the connection
1283: * is alive).
1284: * If an error occurs the return value will be the offset+1 in
1285: * argv[] of the file that caused a problem (i.e, argv[x]
1286: * returns x+1)
1287: * Otherwise, 0 is returned if all files retrieved successfully.
1288: */
1289: int
1.50 deraadt 1290: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 1291: {
1292: char *xargv[5];
1.62 ray 1293: char *cp, *url, *host, *dir, *file, *portnum;
1294: char *username, *pass, *pathstart;
1.6 millert 1295: char *ftpproxy, *httpproxy;
1.14 millert 1296: int rval, xargc;
1297: volatile int argpos;
1.49 krw 1298: int dirhasglob, filehasglob, oautologin;
1.14 millert 1299: char rempath[MAXPATHLEN];
1.1 millert 1300:
1301: argpos = 0;
1302:
1303: if (setjmp(toplevel)) {
1304: if (connected)
1305: disconnect(0, NULL);
1.2 millert 1306: return (argpos + 1);
1.1 millert 1307: }
1.3 millert 1308: (void)signal(SIGINT, (sig_t)intr);
1309: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 1310:
1.45 millert 1311: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
1312: ftpproxy = NULL;
1313: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
1314: httpproxy = NULL;
1.6 millert 1315:
1.1 millert 1316: /*
1317: * Loop through as long as there's files to fetch.
1318: */
1.62 ray 1319: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 1320: if (strchr(argv[argpos], ':') == NULL)
1321: break;
1.62 ray 1322: host = dir = file = portnum = username = pass = NULL;
1.1 millert 1323:
1324: /*
1325: * We muck with the string, so we make a copy.
1326: */
1.62 ray 1327: url = strdup(argv[argpos]);
1328: if (url == NULL)
1.1 millert 1329: errx(1, "Can't allocate memory for auto-fetch.");
1330:
1331: /*
1332: * Try HTTP URL-style arguments first.
1333: */
1.62 ray 1334: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1335: #ifndef SMALL
1336: /* even if we compiled without SSL, url_get will check */
1.62 ray 1337: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 1338: #endif /* !SMALL */
1.62 ray 1339: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 1340: redirect_loop = 0;
1.62 ray 1341: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 1342: rval = argpos + 1;
1343: continue;
1344: }
1345:
1346: /*
1.6 millert 1347: * Try FTP URL-style arguments next. If ftpproxy is
1348: * set, use url_get() instead of standard ftp.
1349: * Finally, try host:file.
1.1 millert 1350: */
1.62 ray 1351: host = url;
1352: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 1353: char *passend, *passagain, *userend;
1.31 itojun 1354:
1.6 millert 1355: if (ftpproxy) {
1.62 ray 1356: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 1357: rval = argpos + 1;
1358: continue;
1359: }
1.1 millert 1360: host += sizeof(FTP_URL) - 1;
1.8 millert 1361: dir = strchr(host, '/');
1.1 millert 1362:
1.8 millert 1363: /* Look for [user:pass@]host[:port] */
1.31 itojun 1364:
1365: /* check if we have "user:pass@" */
1.37 heko 1366: userend = strchr(host, ':');
1.31 itojun 1367: passend = strchr(host, '@');
1368: if (passend && userend && userend < passend &&
1369: (!dir || passend < dir)) {
1.62 ray 1370: username = host;
1.31 itojun 1371: pass = userend + 1;
1372: host = passend + 1;
1373: *userend = *passend = '\0';
1.37 heko 1374: passagain = strchr(host, '@');
1.42 deraadt 1375: if (strchr(pass, '@') != NULL ||
1.37 heko 1376: (passagain != NULL && passagain < dir)) {
1377: warnx(at_encoding_warning);
1378: goto bad_ftp_url;
1.42 deraadt 1379: }
1.31 itojun 1380:
1.77 martynas 1381: if (EMPTYSTRING(username)) {
1.11 millert 1382: bad_ftp_url:
1.31 itojun 1383: warnx("Invalid URL: %s", argv[argpos]);
1384: rval = argpos + 1;
1385: continue;
1386: }
1.62 ray 1387: username = urldecode(username);
1.37 heko 1388: pass = urldecode(pass);
1.8 millert 1389: }
1.31 itojun 1390:
1391: #ifdef INET6
1392: /* check [host]:port, or [host] */
1393: if (host[0] == '[') {
1394: cp = strchr(host, ']');
1395: if (cp && (!dir || cp < dir)) {
1396: if (cp + 1 == dir || cp[1] == ':') {
1397: host++;
1398: *cp++ = '\0';
1399: } else
1400: cp = NULL;
1401: } else
1402: cp = host;
1.25 itojun 1403: } else
1404: cp = host;
1.31 itojun 1405: #else
1406: cp = host;
1.25 itojun 1407: #endif
1.31 itojun 1408:
1409: /* split off host[:port] if there is */
1410: if (cp) {
1411: portnum = strchr(cp, ':');
1.52 henning 1412: pathstart = strchr(cp, '/');
1413: /* : in path is not a port # indicator */
1414: if (portnum && pathstart &&
1415: pathstart < portnum)
1416: portnum = NULL;
1417:
1.31 itojun 1418: if (!portnum)
1419: ;
1420: else {
1421: if (!dir)
1422: ;
1423: else if (portnum + 1 < dir) {
1424: *portnum++ = '\0';
1425: /*
1426: * XXX should check if portnum
1427: * is decimal number
1428: */
1429: } else {
1430: /* empty portnum */
1431: goto bad_ftp_url;
1432: }
1433: }
1434: } else
1435: portnum = NULL;
1.8 millert 1436: } else { /* classic style `host:file' */
1437: dir = strchr(host, ':');
1438: }
1.1 millert 1439: if (EMPTYSTRING(host)) {
1440: rval = argpos + 1;
1441: continue;
1442: }
1443:
1444: /*
1.9 millert 1445: * If dir is NULL, the file wasn't specified
1.1 millert 1446: * (URL looked something like ftp://host)
1447: */
1.8 millert 1448: if (dir != NULL)
1449: *dir++ = '\0';
1.1 millert 1450:
1451: /*
1452: * Extract the file and (if present) directory name.
1453: */
1.42 deraadt 1454: if (!EMPTYSTRING(dir)) {
1.8 millert 1455: cp = strrchr(dir, '/');
1.1 millert 1456: if (cp != NULL) {
1457: *cp++ = '\0';
1458: file = cp;
1459: } else {
1460: file = dir;
1461: dir = NULL;
1462: }
1463: }
1.80 martynas 1464: #ifndef SMALL
1.1 millert 1465: if (debug)
1.42 deraadt 1466: fprintf(ttyout,
1467: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1468: username, pass ? "XXXX" : NULL, host, portnum,
1469: dir, file);
1.80 martynas 1470: #endif /* !SMALL */
1.1 millert 1471:
1472: /*
1.49 krw 1473: * Set up the connection.
1.1 millert 1474: */
1.49 krw 1475: if (connected)
1476: disconnect(0, NULL);
1477: xargv[0] = __progname;
1478: xargv[1] = host;
1.8 millert 1479: xargv[2] = NULL;
1.49 krw 1480: xargc = 2;
1481: if (!EMPTYSTRING(portnum)) {
1482: xargv[2] = portnum;
1483: xargv[3] = NULL;
1484: xargc = 3;
1485: }
1486: oautologin = autologin;
1.88 martynas 1487: if (username == NULL)
1.89 halex 1488: anonftp = 1;
1489: else {
1490: anonftp = 0;
1.49 krw 1491: autologin = 0;
1.89 halex 1492: }
1.49 krw 1493: setpeer(xargc, xargv);
1494: autologin = oautologin;
1.87 martynas 1495: if (connected == 0 ||
1496: (connected == 1 && autologin && (username == NULL ||
1497: !ftp_login(host, username, pass)))) {
1.49 krw 1498: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1499: rval = argpos + 1;
1500: continue;
1.1 millert 1501: }
1.49 krw 1502:
1503: /* Always use binary transfers. */
1504: setbinary(0, NULL);
1.1 millert 1505:
1.4 millert 1506: dirhasglob = filehasglob = 0;
1507: if (doglob) {
1.42 deraadt 1508: if (!EMPTYSTRING(dir) &&
1.4 millert 1509: strpbrk(dir, "*?[]{}") != NULL)
1510: dirhasglob = 1;
1.42 deraadt 1511: if (!EMPTYSTRING(file) &&
1.4 millert 1512: strpbrk(file, "*?[]{}") != NULL)
1513: filehasglob = 1;
1514: }
1515:
1.1 millert 1516: /* Change directories, if necessary. */
1.42 deraadt 1517: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1518: xargv[0] = "cd";
1519: xargv[1] = dir;
1520: xargv[2] = NULL;
1521: cd(2, xargv);
1.42 deraadt 1522: if (!dirchange) {
1.1 millert 1523: rval = argpos + 1;
1524: continue;
1525: }
1526: }
1527:
1528: if (EMPTYSTRING(file)) {
1.86 martynas 1529: #ifndef SMALL
1.1 millert 1530: rval = -1;
1.86 martynas 1531: #else /* !SMALL */
1532: recvrequest("NLST", "-", NULL, "w", 0, 0);
1533: rval = 0;
1534: #endif /* !SMALL */
1.1 millert 1535: continue;
1536: }
1537:
1.21 marc 1538: if (verbose)
1.10 deraadt 1539: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1540:
1.4 millert 1541: if (dirhasglob) {
1542: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1543: file = rempath;
1544: }
1545:
1546: /* Fetch the file(s). */
1.10 deraadt 1547: xargc = 2;
1.1 millert 1548: xargv[0] = "get";
1549: xargv[1] = file;
1550: xargv[2] = NULL;
1.4 millert 1551: if (dirhasglob || filehasglob) {
1552: int ointeractive;
1553:
1554: ointeractive = interactive;
1555: interactive = 0;
1556: xargv[0] = "mget";
1.78 martynas 1557: #ifndef SMALL
1558: if (resume) {
1559: xargc = 3;
1560: xargv[1] = "-c";
1561: xargv[2] = file;
1562: xargv[3] = NULL;
1563: }
1564: #endif /* !SMALL */
1.10 deraadt 1565: mget(xargc, xargv);
1.5 millert 1566: interactive = ointeractive;
1.10 deraadt 1567: } else {
1.17 millert 1568: if (outfile != NULL) {
1569: xargv[2] = outfile;
1570: xargv[3] = NULL;
1.10 deraadt 1571: xargc++;
1572: }
1.75 martynas 1573: #ifndef SMALL
1574: if (resume)
1575: reget(xargc, xargv);
1576: else
1.78 martynas 1577: #endif /* !SMALL */
1.75 martynas 1578: get(xargc, xargv);
1.10 deraadt 1579: }
1.1 millert 1580:
1.4 millert 1581: if ((code / 100) != COMPLETE)
1.1 millert 1582: rval = argpos + 1;
1583: }
1584: if (connected && rval != -1)
1585: disconnect(0, NULL);
1586: return (rval);
1.37 heko 1587: }
1588:
1589: char *
1.50 deraadt 1590: urldecode(const char *str)
1.37 heko 1591: {
1.53 deraadt 1592: char *ret, c;
1593: int i, reallen;
1.37 heko 1594:
1.53 deraadt 1595: if (str == NULL)
1596: return NULL;
1597: if ((ret = malloc(strlen(str)+1)) == NULL)
1598: err(1, "Can't allocate memory for URL decoding");
1599: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1600: c = str[i];
1601: if (c == '+') {
1602: *ret = ' ';
1603: continue;
1604: }
1.61 deraadt 1605:
1606: /* Cannot use strtol here because next char
1607: * after %xx may be a digit.
1608: */
1.53 deraadt 1609: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1610: *ret = hextochar(&str[i+1]);
1611: i+=2;
1612: continue;
1613: }
1614: *ret = c;
1615: }
1616: *ret = '\0';
1617:
1618: return ret-reallen;
1.37 heko 1619: }
1620:
1621: char
1.50 deraadt 1622: hextochar(const char *str)
1.37 heko 1623: {
1.53 deraadt 1624: char c, ret;
1.37 heko 1625:
1.53 deraadt 1626: c = str[0];
1627: ret = c;
1628: if (isalpha(c))
1629: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1630: else
1631: ret -= '0';
1632: ret *= 16;
1633:
1634: c = str[1];
1635: ret += c;
1636: if (isalpha(c))
1637: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1638: else
1639: ret -= '0';
1640: return ret;
1.25 itojun 1641: }
1642:
1643: int
1.50 deraadt 1644: isurl(const char *p)
1.25 itojun 1645: {
1.27 millert 1646:
1.26 deraadt 1647: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1648: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1649: #ifndef SMALL
1650: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1651: #endif /* !SMALL */
1.27 millert 1652: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1653: strstr(p, ":/"))
1654: return (1);
1655: return (0);
1.1 millert 1656: }
1.61 deraadt 1657:
1658: char *
1659: ftp_readline(FILE *fp, SSL *ssl, size_t *lenp)
1660: {
1661: if (fp != NULL)
1662: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1663: #ifndef SMALL
1664: else if (ssl != NULL)
1665: return SSL_readline(ssl, lenp);
1.78 martynas 1666: #endif /* !SMALL */
1.61 deraadt 1667: else
1668: return NULL;
1669: }
1670:
1.65 ray 1671: size_t
1.61 deraadt 1672: ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len)
1673: {
1.65 ray 1674: size_t ret;
1.61 deraadt 1675: if (fp != NULL)
1676: ret = fread(buf, sizeof(char), len, fp);
1677: #ifndef SMALL
1.65 ray 1678: else if (ssl != NULL) {
1679: int nr;
1680:
1681: if (len > INT_MAX)
1682: len = INT_MAX;
1683: if ((nr = SSL_read(ssl, buf, (int)len)) <= 0)
1684: ret = 0;
1685: else
1686: ret = nr;
1687: }
1.78 martynas 1688: #endif /* !SMALL */
1.61 deraadt 1689: else
1690: ret = 0;
1691: return (ret);
1692: }
1693:
1694: int
1695: ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...)
1696: {
1697: int ret;
1698: va_list ap;
1699:
1700: va_start(ap, fmt);
1701:
1702: if (fp != NULL)
1703: ret = vfprintf(fp, fmt, ap);
1704: #ifndef SMALL
1705: else if (ssl != NULL)
1706: ret = SSL_vprintf((SSL*)ssl, fmt, ap);
1.78 martynas 1707: #endif /* !SMALL */
1.61 deraadt 1708: else
1.92 jsg 1709: ret = 0;
1.61 deraadt 1710:
1711: va_end(ap);
1712: return (ret);
1713: }
1714:
1715: #ifndef SMALL
1716: int
1717: SSL_vprintf(SSL *ssl, const char *fmt, va_list ap)
1718: {
1719: int ret;
1720: char *string;
1721:
1722: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1723: return ret;
1.64 ray 1724: ret = SSL_write(ssl, string, ret);
1.61 deraadt 1725: free(string);
1726: return ret;
1727: }
1728:
1729: char *
1730: SSL_readline(SSL *ssl, size_t *lenp)
1731: {
1.63 ray 1732: size_t i, len;
1.61 deraadt 1733: char *buf, *q, c;
1.109 sthen 1734: int ret;
1.61 deraadt 1735:
1736: len = 128;
1737: if ((buf = malloc(len)) == NULL)
1738: errx(1, "Can't allocate memory for transfer buffer");
1739: for (i = 0; ; i++) {
1740: if (i >= len - 1) {
1741: if ((q = realloc(buf, 2 * len)) == NULL)
1742: errx(1, "Can't expand transfer buffer");
1743: buf = q;
1744: len *= 2;
1745: }
1.109 sthen 1746: again:
1747: ret = SSL_read(ssl, &c, 1);
1748: if (ret <= 0) {
1749: if (SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ)
1750: goto again;
1751: else
1752: errx(1, "SSL_read error: %u",
1753: SSL_get_error(ssl, ret));
1754: }
1.61 deraadt 1755: buf[i] = c;
1756: if (c == '\n')
1757: break;
1758: }
1759: *lenp = i;
1760: return (buf);
1761: }
1762:
1763: int
1.81 espie 1764: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1765: {
1.66 ray 1766: int l;
1.61 deraadt 1767: char buf[1024];
1768: char *connstr, *hosttail, *port;
1769:
1770: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1771: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1772: host++;
1773: *hosttail++ = '\0';
1774: } else
1775: hosttail = host;
1776:
1777: port = strrchr(hosttail, ':'); /* find portnum */
1778: if (port != NULL)
1779: *port++ = '\0';
1780: if (!port)
1781: port = "443";
1782:
1.81 espie 1783: if (cookie) {
1784: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1785: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1786: host, port, cookie, HTTP_USER_AGENT);
1787: } else {
1788: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1789: host, port, HTTP_USER_AGENT);
1790: }
1791:
1.66 ray 1792: if (l == -1)
1.61 deraadt 1793: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1794: #ifndef SMALL
1.68 ray 1795: if (debug)
1796: printf("%s", connstr);
1.80 martynas 1797: #endif /* !SMALL */
1.66 ray 1798: if (write(socket, connstr, l) != l)
1.68 ray 1799: err(1, "Could not send connect string");
1.61 deraadt 1800: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1801: free(connstr);
1.61 deraadt 1802: return(200);
1803: }
1.78 martynas 1804: #endif /* !SMALL */