Annotation of src/usr.bin/ftp/fetch.c, Revision 1.120
1.120 ! jca 1: /* $OpenBSD: fetch.c,v 1.119 2014/05/19 20:03:16 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) {
726: warnx("%s: %s", gai_strerror(error), host);
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) {
735: warnx("%s: %s", gai_strerror(error), srcaddr);
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) {
859: fprintf(ttyout, "%s: host `%s' not present in"
860: " server certificate\n",
861: getprogname(), host);
862: goto cleanup_url_get;
863: }
864:
865: X509_free(cert);
1.61 deraadt 866: }
867: } else {
868: fin = fdopen(s, "r+");
869: }
1.78 martynas 870: #else /* !SMALL */
1.40 fgsch 871: fin = fdopen(s, "r+");
1.78 martynas 872: #endif /* !SMALL */
1.40 fgsch 873:
1.55 fgsch 874: if (verbose)
875: fprintf(ttyout, "Requesting %s", origline);
1.95 martynas 876:
1.1 millert 877: /*
1.40 fgsch 878: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 879: */
1.74 pyr 880: #ifndef SMALL
881: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 882: #endif /* !SMALL */
1.95 martynas 883:
884: epath = url_encode(path);
1.62 ray 885: if (proxyurl) {
1.55 fgsch 886: if (verbose)
1.81 espie 887: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 888: /*
889: * Host: directive must use the destination host address for
890: * the original URI (path). We do not attach it at this moment.
891: */
1.73 drahn 892: if (cookie)
893: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 894: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
1.95 martynas 895: epath, cookie, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 896: else
1.74 pyr 897: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
1.95 martynas 898: epath, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 899:
1.28 itojun 900: } else {
1.90 martynas 901: #ifndef SMALL
902: if (resume) {
903: struct stat stbuf;
904:
905: if (stat(savefile, &stbuf) == 0)
906: restart_point = stbuf.st_size;
907: else
908: restart_point = 0;
909: }
1.106 haesbaer 910: if (auth) {
911: ftp_printf(fin, ssl,
912: "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ",
913: epath, restart_point ?
914: "HTTP/1.1\r\nConnection: close" : "HTTP/1.0",
915: auth);
916: free(auth);
917: auth = NULL;
918: } else
919: #endif /* SMALL */
1.107 haesbaer 920: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
921: #ifndef SMALL
922: restart_point ? "HTTP/1.1\r\nConnection: close" :
923: #endif /* !SMALL */
924: "HTTP/1.0");
1.32 itojun 925: if (strchr(host, ':')) {
1.55 fgsch 926: /*
927: * strip off scoped address portion, since it's
928: * local to node
929: */
1.32 itojun 930: h = strdup(host);
931: if (h == NULL)
932: errx(1, "Can't allocate memory.");
933: if ((p = strchr(h, '%')) != NULL)
934: *p = '\0';
1.61 deraadt 935: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 936: free(h);
1.55 fgsch 937: } else
1.61 deraadt 938: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 939:
940: /*
941: * Send port number only if it's specified and does not equal
942: * 80. Some broken HTTP servers get confused if you explicitly
943: * send them the port number.
944: */
1.61 deraadt 945: #ifndef SMALL
946: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
947: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 948: if (restart_point)
1.75 martynas 949: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
950: (long long)restart_point);
1.78 martynas 951: #else /* !SMALL */
1.55 fgsch 952: if (port && strcmp(port, "80") != 0)
1.61 deraadt 953: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 954: #endif /* !SMALL */
1.74 pyr 955: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
956: buf ? buf : "", HTTP_USER_AGENT);
1.55 fgsch 957: if (verbose)
958: fprintf(ttyout, "\n");
1.28 itojun 959: }
1.95 martynas 960: free(epath);
1.74 pyr 961:
962: #ifndef SMALL
963: free(buf);
1.78 martynas 964: #endif /* !SMALL */
1.74 pyr 965: buf = NULL;
966:
1.61 deraadt 967: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 968: warn("Writing HTTP request");
1.6 millert 969: goto cleanup_url_get;
1.1 millert 970: }
1.61 deraadt 971: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 972: warn("Receiving HTTP reply");
973: goto cleanup_url_get;
1.1 millert 974: }
1.40 fgsch 975:
976: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
977: buf[--len] = '\0';
1.80 martynas 978: #ifndef SMALL
1.40 fgsch 979: if (debug)
980: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 981: #endif /* !SMALL */
1.40 fgsch 982:
1.1 millert 983: cp = strchr(buf, ' ');
984: if (cp == NULL)
985: goto improper;
986: else
987: cp++;
1.69 jsg 988:
989: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 990: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 991: if (errstr) {
992: warnx("Error retrieving file: %s", cp);
993: goto cleanup_url_get;
994: }
995:
996: switch (status) {
997: case 200: /* OK */
1.75 martynas 998: #ifndef SMALL
1.109 sthen 999: /*
1.98 phessler 1000: * When we request a partial file, and we receive an HTTP 200
1001: * it is a good indication that the server doesn't support
1002: * range requests, and is about to send us the entire file.
1003: * If the restart_point == 0, then we are not actually
1004: * requesting a partial file, and an HTTP 200 is appropriate.
1005: */
1006: if (resume && restart_point != 0) {
1007: warnx("Server does not support resume.");
1008: restart_point = resume = 0;
1009: }
1.101 halex 1010: /* FALLTHROUGH */
1.75 martynas 1011: case 206: /* Partial Content */
1.91 halex 1012: #endif /* !SMALL */
1.69 jsg 1013: break;
1014: case 301: /* Moved Permanently */
1015: case 302: /* Found */
1016: case 303: /* See Other */
1017: case 307: /* Temporary Redirect */
1.40 fgsch 1018: isredirect++;
1.54 fgsch 1019: if (redirect_loop++ > 10) {
1020: warnx("Too many redirections requested");
1021: goto cleanup_url_get;
1022: }
1.69 jsg 1023: break;
1.75 martynas 1024: #ifndef SMALL
1025: case 416: /* Requested Range Not Satisfiable */
1026: warnx("File is already fully retrieved.");
1027: goto cleanup_url_get;
1.78 martynas 1028: #endif /* !SMALL */
1.69 jsg 1029: default:
1.1 millert 1030: warnx("Error retrieving file: %s", cp);
1.6 millert 1031: goto cleanup_url_get;
1.1 millert 1032: }
1033:
1034: /*
1035: * Read the rest of the header.
1036: */
1.40 fgsch 1037: free(buf);
1038: filesize = -1;
1039:
1.62 ray 1040: for (;;) {
1.61 deraadt 1041: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 1042: warn("Receiving HTTP reply");
1043: goto cleanup_url_get;
1044: }
1.61 deraadt 1045:
1.40 fgsch 1046: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
1047: buf[--len] = '\0';
1048: if (len == 0)
1.1 millert 1049: break;
1.80 martynas 1050: #ifndef SMALL
1.40 fgsch 1051: if (debug)
1052: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 1053: #endif /* !SMALL */
1.1 millert 1054:
1.40 fgsch 1055: /* Look for some headers */
1056: cp = buf;
1.1 millert 1057: #define CONTENTLEN "Content-Length: "
1.40 fgsch 1058: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
1.104 sthen 1059: size_t s;
1.40 fgsch 1060: cp += sizeof(CONTENTLEN) - 1;
1.111 deraadt 1061: if ((s = strcspn(cp, " \t")))
1.104 sthen 1062: *(cp+s) = 0;
1.58 grunk 1063: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
1064: if (errstr != NULL)
1.40 fgsch 1065: goto improper;
1.75 martynas 1066: #ifndef SMALL
1.90 martynas 1067: if (restart_point)
1.75 martynas 1068: filesize += restart_point;
1.78 martynas 1069: #endif /* !SMALL */
1.40 fgsch 1070: #define LOCATION "Location: "
1071: } else if (isredirect &&
1072: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
1073: cp += sizeof(LOCATION) - 1;
1.102 halex 1074: if (strstr(cp, "://") == NULL) {
1075: #ifdef SMALL
1076: errx(1, "Relative redirect not supported");
1077: #else /* SMALL */
1078: if (*cp == '/') {
1079: locbase = NULL;
1080: cp++;
1081: } else {
1082: locbase = strdup(path);
1083: if (locbase == NULL)
1084: errx(1, "Can't allocate memory"
1085: " for location base");
1086: loctail = strchr(locbase, '#');
1087: if (loctail != NULL)
1088: *loctail = '\0';
1089: loctail = strchr(locbase, '?');
1090: if (loctail != NULL)
1091: *loctail = '\0';
1092: loctail = strrchr(locbase, '/');
1093: if (loctail == NULL) {
1094: free(locbase);
1095: locbase = NULL;
1096: } else
1097: loctail[1] = '\0';
1098: }
1099: /* Contruct URL from relative redirect */
1100: if (asprintf(&redirurl, "%s%s%s%s/%s%s",
1101: scheme, full_host,
1102: portnum ? ":" : "",
1103: portnum ? portnum : "",
1104: locbase ? locbase : "",
1105: cp) == -1)
1106: errx(1, "Cannot build "
1107: "redirect URL");
1108: free(locbase);
1109: #endif /* SMALL */
1110: } else if ((redirurl = strdup(cp)) == NULL)
1111: errx(1, "Cannot allocate memory for URL");
1112: loctail = strchr(redirurl, '#');
1113: if (loctail != NULL)
1114: *loctail = '\0';
1.40 fgsch 1115: if (verbose)
1.102 halex 1116: fprintf(ttyout, "Redirected to %s\n", redirurl);
1.40 fgsch 1117: if (fin != NULL)
1118: fclose(fin);
1119: else if (s != -1)
1120: close(s);
1.102 halex 1121: rval = url_get(redirurl, proxyenv, savefile);
1122: free(redirurl);
1123: goto cleanup_url_get;
1.40 fgsch 1124: }
1.108 tobias 1125: free(buf);
1.1 millert 1126: }
1127:
1.17 millert 1128: /* Open the output file. */
1.100 halex 1129: if (!pipeout) {
1.75 martynas 1130: #ifndef SMALL
1131: if (resume)
1.83 martynas 1132: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
1133: 0666);
1.75 martynas 1134: else
1.78 martynas 1135: #endif /* !SMALL */
1.75 martynas 1136: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
1137: 0666);
1.10 deraadt 1138: if (out < 0) {
1139: warn("Can't open %s", savefile);
1140: goto cleanup_url_get;
1141: }
1142: } else
1.17 millert 1143: out = fileno(stdout);
1.1 millert 1144:
1145: /* Trap signals */
1146: oldintr = NULL;
1.97 martynas 1147: oldinti = NULL;
1.1 millert 1148: if (setjmp(httpabort)) {
1149: if (oldintr)
1.2 millert 1150: (void)signal(SIGINT, oldintr);
1.97 martynas 1151: if (oldinti)
1152: (void)signal(SIGINFO, oldinti);
1.6 millert 1153: goto cleanup_url_get;
1.1 millert 1154: }
1155: oldintr = signal(SIGINT, aborthttp);
1156:
1157: bytes = 0;
1158: hashbytes = mark;
1.84 martynas 1159: progressmeter(-1, path);
1.43 millert 1160:
1161: free(buf);
1.1 millert 1162:
1163: /* Finally, suck down the file. */
1.113 tedu 1164: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 1165: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 1166: i = 0;
1.61 deraadt 1167: len = 1;
1.97 martynas 1168: oldinti = signal(SIGINFO, psummary);
1.61 deraadt 1169: while (len > 0) {
1.113 tedu 1170: len = ftp_read(fin, ssl, buf, buflen);
1.1 millert 1171: bytes += len;
1.61 deraadt 1172: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
1173: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 1174: warn("Writing %s", savefile);
1.97 martynas 1175: signal(SIGINFO, oldinti);
1.6 millert 1176: goto cleanup_url_get;
1.1 millert 1177: }
1178: else if (i == 0)
1179: break;
1180: }
1181: if (hash && !progress) {
1182: while (bytes >= hashbytes) {
1.10 deraadt 1183: (void)putc('#', ttyout);
1.1 millert 1184: hashbytes += mark;
1185: }
1.10 deraadt 1186: (void)fflush(ttyout);
1.1 millert 1187: }
1188: }
1.97 martynas 1189: signal(SIGINFO, oldinti);
1.1 millert 1190: if (hash && !progress && bytes > 0) {
1191: if (bytes < mark)
1.10 deraadt 1192: (void)putc('#', ttyout);
1193: (void)putc('\n', ttyout);
1194: (void)fflush(ttyout);
1.1 millert 1195: }
1196: if (len != 0) {
1197: warn("Reading from socket");
1.6 millert 1198: goto cleanup_url_get;
1.1 millert 1199: }
1.84 martynas 1200: progressmeter(1, NULL);
1.75 martynas 1201: if (
1202: #ifndef SMALL
1203: !resume &&
1.78 martynas 1204: #endif /* !SMALL */
1.75 martynas 1205: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 1206: if (verbose)
1207: fputs("Read short file.\n", ttyout);
1208: goto cleanup_url_get;
1209: }
1210:
1.1 millert 1211: if (verbose)
1.97 martynas 1212: ptransfer(0);
1.2 millert 1213: (void)signal(SIGINT, oldintr);
1.1 millert 1214:
1.40 fgsch 1215: rval = 0;
1216: goto cleanup_url_get;
1.1 millert 1217:
1.14 millert 1218: noftpautologin:
1219: warnx(
1220: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
1221: goto cleanup_url_get;
1222:
1.1 millert 1223: improper:
1.8 millert 1224: warnx("Improper response from %s", host);
1.14 millert 1225:
1.6 millert 1226: cleanup_url_get:
1.61 deraadt 1227: #ifndef SMALL
1228: if (ssl) {
1229: SSL_shutdown(ssl);
1230: SSL_free(ssl);
1231: }
1.102 halex 1232: free(full_host);
1.106 haesbaer 1233: free(auth);
1.78 martynas 1234: #endif /* !SMALL */
1.40 fgsch 1235: if (fin != NULL)
1236: fclose(fin);
1237: else if (s != -1)
1.1 millert 1238: close(s);
1.67 steven 1239: free(buf);
1240: free(proxyurl);
1.62 ray 1241: free(newline);
1.81 espie 1242: free(cookie);
1.40 fgsch 1243: return (rval);
1.1 millert 1244: }
1245:
1246: /*
1247: * Abort a http retrieval
1248: */
1.51 deraadt 1249: /* ARGSUSED */
1.1 millert 1250: void
1.51 deraadt 1251: aborthttp(int signo)
1.1 millert 1252: {
1253:
1254: alarmtimer(0);
1.10 deraadt 1255: fputs("\nhttp fetch aborted.\n", ttyout);
1256: (void)fflush(ttyout);
1.1 millert 1257: longjmp(httpabort, 1);
1258: }
1259:
1260: /*
1.22 deraadt 1261: * Abort a http retrieval
1262: */
1.51 deraadt 1263: /* ARGSUSED */
1.22 deraadt 1264: void
1.51 deraadt 1265: abortfile(int signo)
1.22 deraadt 1266: {
1267:
1268: alarmtimer(0);
1269: fputs("\nfile fetch aborted.\n", ttyout);
1270: (void)fflush(ttyout);
1271: longjmp(httpabort, 1);
1272: }
1273:
1274: /*
1.1 millert 1275: * Retrieve multiple files from the command line, transferring
1276: * files of the form "host:path", "ftp://host/path" using the
1277: * ftp protocol, and files of the form "http://host/path" using
1278: * the http protocol.
1.2 millert 1279: * If path has a trailing "/", then return (-1);
1.1 millert 1280: * the path will be cd-ed into and the connection remains open,
1281: * and the function will return -1 (to indicate the connection
1282: * is alive).
1283: * If an error occurs the return value will be the offset+1 in
1284: * argv[] of the file that caused a problem (i.e, argv[x]
1285: * returns x+1)
1286: * Otherwise, 0 is returned if all files retrieved successfully.
1287: */
1288: int
1.50 deraadt 1289: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 1290: {
1291: char *xargv[5];
1.62 ray 1292: char *cp, *url, *host, *dir, *file, *portnum;
1293: char *username, *pass, *pathstart;
1.6 millert 1294: char *ftpproxy, *httpproxy;
1.14 millert 1295: int rval, xargc;
1296: volatile int argpos;
1.49 krw 1297: int dirhasglob, filehasglob, oautologin;
1.14 millert 1298: char rempath[MAXPATHLEN];
1.1 millert 1299:
1300: argpos = 0;
1301:
1302: if (setjmp(toplevel)) {
1303: if (connected)
1304: disconnect(0, NULL);
1.2 millert 1305: return (argpos + 1);
1.1 millert 1306: }
1.3 millert 1307: (void)signal(SIGINT, (sig_t)intr);
1308: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 1309:
1.45 millert 1310: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
1311: ftpproxy = NULL;
1312: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
1313: httpproxy = NULL;
1.6 millert 1314:
1.1 millert 1315: /*
1316: * Loop through as long as there's files to fetch.
1317: */
1.62 ray 1318: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 1319: if (strchr(argv[argpos], ':') == NULL)
1320: break;
1.62 ray 1321: host = dir = file = portnum = username = pass = NULL;
1.1 millert 1322:
1323: /*
1324: * We muck with the string, so we make a copy.
1325: */
1.62 ray 1326: url = strdup(argv[argpos]);
1327: if (url == NULL)
1.1 millert 1328: errx(1, "Can't allocate memory for auto-fetch.");
1329:
1330: /*
1331: * Try HTTP URL-style arguments first.
1332: */
1.62 ray 1333: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1334: #ifndef SMALL
1335: /* even if we compiled without SSL, url_get will check */
1.62 ray 1336: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 1337: #endif /* !SMALL */
1.62 ray 1338: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 1339: redirect_loop = 0;
1.62 ray 1340: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 1341: rval = argpos + 1;
1342: continue;
1343: }
1344:
1345: /*
1.6 millert 1346: * Try FTP URL-style arguments next. If ftpproxy is
1347: * set, use url_get() instead of standard ftp.
1348: * Finally, try host:file.
1.1 millert 1349: */
1.62 ray 1350: host = url;
1351: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 1352: char *passend, *passagain, *userend;
1.31 itojun 1353:
1.6 millert 1354: if (ftpproxy) {
1.62 ray 1355: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 1356: rval = argpos + 1;
1357: continue;
1358: }
1.1 millert 1359: host += sizeof(FTP_URL) - 1;
1.8 millert 1360: dir = strchr(host, '/');
1.1 millert 1361:
1.8 millert 1362: /* Look for [user:pass@]host[:port] */
1.31 itojun 1363:
1364: /* check if we have "user:pass@" */
1.37 heko 1365: userend = strchr(host, ':');
1.31 itojun 1366: passend = strchr(host, '@');
1367: if (passend && userend && userend < passend &&
1368: (!dir || passend < dir)) {
1.62 ray 1369: username = host;
1.31 itojun 1370: pass = userend + 1;
1371: host = passend + 1;
1372: *userend = *passend = '\0';
1.37 heko 1373: passagain = strchr(host, '@');
1.42 deraadt 1374: if (strchr(pass, '@') != NULL ||
1.37 heko 1375: (passagain != NULL && passagain < dir)) {
1376: warnx(at_encoding_warning);
1377: goto bad_ftp_url;
1.42 deraadt 1378: }
1.31 itojun 1379:
1.77 martynas 1380: if (EMPTYSTRING(username)) {
1.11 millert 1381: bad_ftp_url:
1.31 itojun 1382: warnx("Invalid URL: %s", argv[argpos]);
1383: rval = argpos + 1;
1384: continue;
1385: }
1.62 ray 1386: username = urldecode(username);
1.37 heko 1387: pass = urldecode(pass);
1.8 millert 1388: }
1.31 itojun 1389:
1390: #ifdef INET6
1391: /* check [host]:port, or [host] */
1392: if (host[0] == '[') {
1393: cp = strchr(host, ']');
1394: if (cp && (!dir || cp < dir)) {
1395: if (cp + 1 == dir || cp[1] == ':') {
1396: host++;
1397: *cp++ = '\0';
1398: } else
1399: cp = NULL;
1400: } else
1401: cp = host;
1.25 itojun 1402: } else
1403: cp = host;
1.31 itojun 1404: #else
1405: cp = host;
1.25 itojun 1406: #endif
1.31 itojun 1407:
1408: /* split off host[:port] if there is */
1409: if (cp) {
1410: portnum = strchr(cp, ':');
1.52 henning 1411: pathstart = strchr(cp, '/');
1412: /* : in path is not a port # indicator */
1413: if (portnum && pathstart &&
1414: pathstart < portnum)
1415: portnum = NULL;
1416:
1.31 itojun 1417: if (!portnum)
1418: ;
1419: else {
1420: if (!dir)
1421: ;
1422: else if (portnum + 1 < dir) {
1423: *portnum++ = '\0';
1424: /*
1425: * XXX should check if portnum
1426: * is decimal number
1427: */
1428: } else {
1429: /* empty portnum */
1430: goto bad_ftp_url;
1431: }
1432: }
1433: } else
1434: portnum = NULL;
1.8 millert 1435: } else { /* classic style `host:file' */
1436: dir = strchr(host, ':');
1437: }
1.1 millert 1438: if (EMPTYSTRING(host)) {
1439: rval = argpos + 1;
1440: continue;
1441: }
1442:
1443: /*
1.9 millert 1444: * If dir is NULL, the file wasn't specified
1.1 millert 1445: * (URL looked something like ftp://host)
1446: */
1.8 millert 1447: if (dir != NULL)
1448: *dir++ = '\0';
1.1 millert 1449:
1450: /*
1451: * Extract the file and (if present) directory name.
1452: */
1.42 deraadt 1453: if (!EMPTYSTRING(dir)) {
1.8 millert 1454: cp = strrchr(dir, '/');
1.1 millert 1455: if (cp != NULL) {
1456: *cp++ = '\0';
1457: file = cp;
1458: } else {
1459: file = dir;
1460: dir = NULL;
1461: }
1462: }
1.80 martynas 1463: #ifndef SMALL
1.1 millert 1464: if (debug)
1.42 deraadt 1465: fprintf(ttyout,
1466: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1467: username, pass ? "XXXX" : NULL, host, portnum,
1468: dir, file);
1.80 martynas 1469: #endif /* !SMALL */
1.1 millert 1470:
1471: /*
1.49 krw 1472: * Set up the connection.
1.1 millert 1473: */
1.49 krw 1474: if (connected)
1475: disconnect(0, NULL);
1476: xargv[0] = __progname;
1477: xargv[1] = host;
1.8 millert 1478: xargv[2] = NULL;
1.49 krw 1479: xargc = 2;
1480: if (!EMPTYSTRING(portnum)) {
1481: xargv[2] = portnum;
1482: xargv[3] = NULL;
1483: xargc = 3;
1484: }
1485: oautologin = autologin;
1.88 martynas 1486: if (username == NULL)
1.89 halex 1487: anonftp = 1;
1488: else {
1489: anonftp = 0;
1.49 krw 1490: autologin = 0;
1.89 halex 1491: }
1.49 krw 1492: setpeer(xargc, xargv);
1493: autologin = oautologin;
1.87 martynas 1494: if (connected == 0 ||
1495: (connected == 1 && autologin && (username == NULL ||
1496: !ftp_login(host, username, pass)))) {
1.49 krw 1497: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1498: rval = argpos + 1;
1499: continue;
1.1 millert 1500: }
1.49 krw 1501:
1502: /* Always use binary transfers. */
1503: setbinary(0, NULL);
1.1 millert 1504:
1.4 millert 1505: dirhasglob = filehasglob = 0;
1506: if (doglob) {
1.42 deraadt 1507: if (!EMPTYSTRING(dir) &&
1.4 millert 1508: strpbrk(dir, "*?[]{}") != NULL)
1509: dirhasglob = 1;
1.42 deraadt 1510: if (!EMPTYSTRING(file) &&
1.4 millert 1511: strpbrk(file, "*?[]{}") != NULL)
1512: filehasglob = 1;
1513: }
1514:
1.1 millert 1515: /* Change directories, if necessary. */
1.42 deraadt 1516: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1517: xargv[0] = "cd";
1518: xargv[1] = dir;
1519: xargv[2] = NULL;
1520: cd(2, xargv);
1.42 deraadt 1521: if (!dirchange) {
1.1 millert 1522: rval = argpos + 1;
1523: continue;
1524: }
1525: }
1526:
1527: if (EMPTYSTRING(file)) {
1.86 martynas 1528: #ifndef SMALL
1.1 millert 1529: rval = -1;
1.86 martynas 1530: #else /* !SMALL */
1531: recvrequest("NLST", "-", NULL, "w", 0, 0);
1532: rval = 0;
1533: #endif /* !SMALL */
1.1 millert 1534: continue;
1535: }
1536:
1.21 marc 1537: if (verbose)
1.10 deraadt 1538: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1539:
1.4 millert 1540: if (dirhasglob) {
1541: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1542: file = rempath;
1543: }
1544:
1545: /* Fetch the file(s). */
1.10 deraadt 1546: xargc = 2;
1.1 millert 1547: xargv[0] = "get";
1548: xargv[1] = file;
1549: xargv[2] = NULL;
1.4 millert 1550: if (dirhasglob || filehasglob) {
1551: int ointeractive;
1552:
1553: ointeractive = interactive;
1554: interactive = 0;
1555: xargv[0] = "mget";
1.78 martynas 1556: #ifndef SMALL
1557: if (resume) {
1558: xargc = 3;
1559: xargv[1] = "-c";
1560: xargv[2] = file;
1561: xargv[3] = NULL;
1562: }
1563: #endif /* !SMALL */
1.10 deraadt 1564: mget(xargc, xargv);
1.5 millert 1565: interactive = ointeractive;
1.10 deraadt 1566: } else {
1.17 millert 1567: if (outfile != NULL) {
1568: xargv[2] = outfile;
1569: xargv[3] = NULL;
1.10 deraadt 1570: xargc++;
1571: }
1.75 martynas 1572: #ifndef SMALL
1573: if (resume)
1574: reget(xargc, xargv);
1575: else
1.78 martynas 1576: #endif /* !SMALL */
1.75 martynas 1577: get(xargc, xargv);
1.10 deraadt 1578: }
1.1 millert 1579:
1.4 millert 1580: if ((code / 100) != COMPLETE)
1.1 millert 1581: rval = argpos + 1;
1582: }
1583: if (connected && rval != -1)
1584: disconnect(0, NULL);
1585: return (rval);
1.37 heko 1586: }
1587:
1588: char *
1.50 deraadt 1589: urldecode(const char *str)
1.37 heko 1590: {
1.53 deraadt 1591: char *ret, c;
1592: int i, reallen;
1.37 heko 1593:
1.53 deraadt 1594: if (str == NULL)
1595: return NULL;
1596: if ((ret = malloc(strlen(str)+1)) == NULL)
1597: err(1, "Can't allocate memory for URL decoding");
1598: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1599: c = str[i];
1600: if (c == '+') {
1601: *ret = ' ';
1602: continue;
1603: }
1.61 deraadt 1604:
1605: /* Cannot use strtol here because next char
1606: * after %xx may be a digit.
1607: */
1.53 deraadt 1608: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1609: *ret = hextochar(&str[i+1]);
1610: i+=2;
1611: continue;
1612: }
1613: *ret = c;
1614: }
1615: *ret = '\0';
1616:
1617: return ret-reallen;
1.37 heko 1618: }
1619:
1620: char
1.50 deraadt 1621: hextochar(const char *str)
1.37 heko 1622: {
1.53 deraadt 1623: char c, ret;
1.37 heko 1624:
1.53 deraadt 1625: c = str[0];
1626: ret = c;
1627: if (isalpha(c))
1628: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1629: else
1630: ret -= '0';
1631: ret *= 16;
1632:
1633: c = str[1];
1634: ret += c;
1635: if (isalpha(c))
1636: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1637: else
1638: ret -= '0';
1639: return ret;
1.25 itojun 1640: }
1641:
1642: int
1.50 deraadt 1643: isurl(const char *p)
1.25 itojun 1644: {
1.27 millert 1645:
1.26 deraadt 1646: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1647: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1648: #ifndef SMALL
1649: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1650: #endif /* !SMALL */
1.27 millert 1651: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1652: strstr(p, ":/"))
1653: return (1);
1654: return (0);
1.1 millert 1655: }
1.61 deraadt 1656:
1657: char *
1658: ftp_readline(FILE *fp, SSL *ssl, size_t *lenp)
1659: {
1660: if (fp != NULL)
1661: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1662: #ifndef SMALL
1663: else if (ssl != NULL)
1664: return SSL_readline(ssl, lenp);
1.78 martynas 1665: #endif /* !SMALL */
1.61 deraadt 1666: else
1667: return NULL;
1668: }
1669:
1.65 ray 1670: size_t
1.61 deraadt 1671: ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len)
1672: {
1.65 ray 1673: size_t ret;
1.61 deraadt 1674: if (fp != NULL)
1675: ret = fread(buf, sizeof(char), len, fp);
1676: #ifndef SMALL
1.65 ray 1677: else if (ssl != NULL) {
1678: int nr;
1679:
1680: if (len > INT_MAX)
1681: len = INT_MAX;
1682: if ((nr = SSL_read(ssl, buf, (int)len)) <= 0)
1683: ret = 0;
1684: else
1685: ret = nr;
1686: }
1.78 martynas 1687: #endif /* !SMALL */
1.61 deraadt 1688: else
1689: ret = 0;
1690: return (ret);
1691: }
1692:
1693: int
1694: ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...)
1695: {
1696: int ret;
1697: va_list ap;
1698:
1699: va_start(ap, fmt);
1700:
1701: if (fp != NULL)
1702: ret = vfprintf(fp, fmt, ap);
1703: #ifndef SMALL
1704: else if (ssl != NULL)
1705: ret = SSL_vprintf((SSL*)ssl, fmt, ap);
1.78 martynas 1706: #endif /* !SMALL */
1.61 deraadt 1707: else
1.92 jsg 1708: ret = 0;
1.61 deraadt 1709:
1710: va_end(ap);
1711: return (ret);
1712: }
1713:
1714: #ifndef SMALL
1715: int
1716: SSL_vprintf(SSL *ssl, const char *fmt, va_list ap)
1717: {
1718: int ret;
1719: char *string;
1720:
1721: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1722: return ret;
1.64 ray 1723: ret = SSL_write(ssl, string, ret);
1.61 deraadt 1724: free(string);
1725: return ret;
1726: }
1727:
1728: char *
1729: SSL_readline(SSL *ssl, size_t *lenp)
1730: {
1.63 ray 1731: size_t i, len;
1.61 deraadt 1732: char *buf, *q, c;
1.109 sthen 1733: int ret;
1.61 deraadt 1734:
1735: len = 128;
1736: if ((buf = malloc(len)) == NULL)
1737: errx(1, "Can't allocate memory for transfer buffer");
1738: for (i = 0; ; i++) {
1739: if (i >= len - 1) {
1740: if ((q = realloc(buf, 2 * len)) == NULL)
1741: errx(1, "Can't expand transfer buffer");
1742: buf = q;
1743: len *= 2;
1744: }
1.109 sthen 1745: again:
1746: ret = SSL_read(ssl, &c, 1);
1747: if (ret <= 0) {
1748: if (SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ)
1749: goto again;
1750: else
1751: errx(1, "SSL_read error: %u",
1752: SSL_get_error(ssl, ret));
1753: }
1.61 deraadt 1754: buf[i] = c;
1755: if (c == '\n')
1756: break;
1757: }
1758: *lenp = i;
1759: return (buf);
1760: }
1761:
1762: int
1.81 espie 1763: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1764: {
1.66 ray 1765: int l;
1.61 deraadt 1766: char buf[1024];
1767: char *connstr, *hosttail, *port;
1768:
1769: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1770: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1771: host++;
1772: *hosttail++ = '\0';
1773: } else
1774: hosttail = host;
1775:
1776: port = strrchr(hosttail, ':'); /* find portnum */
1777: if (port != NULL)
1778: *port++ = '\0';
1779: if (!port)
1780: port = "443";
1781:
1.81 espie 1782: if (cookie) {
1783: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1784: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1785: host, port, cookie, HTTP_USER_AGENT);
1786: } else {
1787: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1788: host, port, HTTP_USER_AGENT);
1789: }
1790:
1.66 ray 1791: if (l == -1)
1.61 deraadt 1792: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1793: #ifndef SMALL
1.68 ray 1794: if (debug)
1795: printf("%s", connstr);
1.80 martynas 1796: #endif /* !SMALL */
1.66 ray 1797: if (write(socket, connstr, l) != l)
1.68 ray 1798: err(1, "Could not send connect string");
1.61 deraadt 1799: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1800: free(connstr);
1.61 deraadt 1801: return(200);
1802: }
1.78 martynas 1803: #endif /* !SMALL */