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