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