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