Annotation of src/usr.bin/ftp/fetch.c, Revision 1.127
1.127 ! jca 1: /* $OpenBSD: fetch.c,v 1.126 2014/07/14 09:26:27 jsing 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.126 jsing 64: #include <ressl.h>
1.78 martynas 65: #else /* !SMALL */
1.126 jsing 66: struct ressl;
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.126 jsing 78: int ftp_printf(FILE *, struct ressl *, const char *, ...) __attribute__((format(printf, 3, 4)));
79: char *ftp_readline(FILE *, struct ressl *, size_t *);
80: size_t ftp_read(FILE *, struct ressl *, char *, size_t);
1.61 deraadt 81: #ifndef SMALL
1.81 espie 82: int proxy_connect(int, char *, char *);
1.126 jsing 83: int SSL_vprintf(struct ressl *, const char *, va_list);
84: char *SSL_readline(struct ressl *, 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.126 jsing 196: struct ressl *ssl = 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.126 jsing 594: if (ressl_init() != 0) {
595: fprintf(ttyout, "SSL initialisation failed\n");
1.116 jca 596: goto cleanup_url_get;
597: }
1.126 jsing 598: if ((ssl = ressl_client()) == NULL) {
599: fprintf(ttyout, "failed to create SSL client\n");
1.61 deraadt 600: goto cleanup_url_get;
601: }
1.126 jsing 602: if (ressl_configure(ssl, ressl_config) != 0) {
603: fprintf(ttyout, "SSL configuration failure: %s\n",
604: ressl_error(ssl));
1.61 deraadt 605: goto cleanup_url_get;
1.117 jca 606: }
1.126 jsing 607: if (ressl_connect_socket(ssl, s, host) != 0) {
608: fprintf(ttyout, "SSL failure: %s\n", ressl_error(ssl));
1.72 ray 609: goto cleanup_url_get;
1.118 jca 610: }
1.61 deraadt 611: } else {
612: fin = fdopen(s, "r+");
613: }
1.78 martynas 614: #else /* !SMALL */
1.40 fgsch 615: fin = fdopen(s, "r+");
1.78 martynas 616: #endif /* !SMALL */
1.40 fgsch 617:
1.55 fgsch 618: if (verbose)
619: fprintf(ttyout, "Requesting %s", origline);
1.95 martynas 620:
1.1 millert 621: /*
1.40 fgsch 622: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 623: */
1.74 pyr 624: #ifndef SMALL
625: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 626: #endif /* !SMALL */
1.95 martynas 627:
628: epath = url_encode(path);
1.62 ray 629: if (proxyurl) {
1.55 fgsch 630: if (verbose)
1.81 espie 631: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 632: /*
633: * Host: directive must use the destination host address for
634: * the original URI (path). We do not attach it at this moment.
635: */
1.123 guenther 636: if (credentials)
1.73 drahn 637: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 638: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
1.123 guenther 639: epath, credentials, buf ? buf : "",
1.124 lteo 640: httpuseragent);
1.73 drahn 641: else
1.74 pyr 642: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
1.124 lteo 643: epath, buf ? buf : "", httpuseragent);
1.73 drahn 644:
1.28 itojun 645: } else {
1.90 martynas 646: #ifndef SMALL
647: if (resume) {
648: struct stat stbuf;
649:
650: if (stat(savefile, &stbuf) == 0)
651: restart_point = stbuf.st_size;
652: else
653: restart_point = 0;
654: }
1.123 guenther 655: if (credentials) {
1.106 haesbaer 656: ftp_printf(fin, ssl,
657: "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ",
658: epath, restart_point ?
659: "HTTP/1.1\r\nConnection: close" : "HTTP/1.0",
1.123 guenther 660: credentials);
661: free(credentials);
662: credentials = NULL;
1.106 haesbaer 663: } else
664: #endif /* SMALL */
1.107 haesbaer 665: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
666: #ifndef SMALL
667: restart_point ? "HTTP/1.1\r\nConnection: close" :
668: #endif /* !SMALL */
669: "HTTP/1.0");
1.32 itojun 670: if (strchr(host, ':')) {
1.55 fgsch 671: /*
672: * strip off scoped address portion, since it's
673: * local to node
674: */
1.32 itojun 675: h = strdup(host);
676: if (h == NULL)
677: errx(1, "Can't allocate memory.");
678: if ((p = strchr(h, '%')) != NULL)
679: *p = '\0';
1.61 deraadt 680: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 681: free(h);
1.55 fgsch 682: } else
1.61 deraadt 683: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 684:
685: /*
686: * Send port number only if it's specified and does not equal
687: * 80. Some broken HTTP servers get confused if you explicitly
688: * send them the port number.
689: */
1.61 deraadt 690: #ifndef SMALL
691: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
692: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 693: if (restart_point)
1.75 martynas 694: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
695: (long long)restart_point);
1.78 martynas 696: #else /* !SMALL */
1.55 fgsch 697: if (port && strcmp(port, "80") != 0)
1.61 deraadt 698: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 699: #endif /* !SMALL */
1.74 pyr 700: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
1.124 lteo 701: buf ? buf : "", httpuseragent);
1.55 fgsch 702: if (verbose)
703: fprintf(ttyout, "\n");
1.28 itojun 704: }
1.95 martynas 705: free(epath);
1.74 pyr 706:
707: #ifndef SMALL
708: free(buf);
1.78 martynas 709: #endif /* !SMALL */
1.74 pyr 710: buf = NULL;
711:
1.61 deraadt 712: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 713: warn("Writing HTTP request");
1.6 millert 714: goto cleanup_url_get;
1.1 millert 715: }
1.61 deraadt 716: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 717: warn("Receiving HTTP reply");
718: goto cleanup_url_get;
1.1 millert 719: }
1.40 fgsch 720:
721: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
722: buf[--len] = '\0';
1.80 martynas 723: #ifndef SMALL
1.40 fgsch 724: if (debug)
725: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 726: #endif /* !SMALL */
1.40 fgsch 727:
1.1 millert 728: cp = strchr(buf, ' ');
729: if (cp == NULL)
730: goto improper;
731: else
732: cp++;
1.69 jsg 733:
734: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 735: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 736: if (errstr) {
737: warnx("Error retrieving file: %s", cp);
738: goto cleanup_url_get;
739: }
740:
741: switch (status) {
742: case 200: /* OK */
1.75 martynas 743: #ifndef SMALL
1.109 sthen 744: /*
1.98 phessler 745: * When we request a partial file, and we receive an HTTP 200
746: * it is a good indication that the server doesn't support
747: * range requests, and is about to send us the entire file.
748: * If the restart_point == 0, then we are not actually
749: * requesting a partial file, and an HTTP 200 is appropriate.
750: */
751: if (resume && restart_point != 0) {
752: warnx("Server does not support resume.");
753: restart_point = resume = 0;
754: }
1.101 halex 755: /* FALLTHROUGH */
1.75 martynas 756: case 206: /* Partial Content */
1.91 halex 757: #endif /* !SMALL */
1.69 jsg 758: break;
759: case 301: /* Moved Permanently */
760: case 302: /* Found */
761: case 303: /* See Other */
762: case 307: /* Temporary Redirect */
1.40 fgsch 763: isredirect++;
1.54 fgsch 764: if (redirect_loop++ > 10) {
765: warnx("Too many redirections requested");
766: goto cleanup_url_get;
767: }
1.69 jsg 768: break;
1.75 martynas 769: #ifndef SMALL
770: case 416: /* Requested Range Not Satisfiable */
771: warnx("File is already fully retrieved.");
772: goto cleanup_url_get;
1.78 martynas 773: #endif /* !SMALL */
1.69 jsg 774: default:
1.1 millert 775: warnx("Error retrieving file: %s", cp);
1.6 millert 776: goto cleanup_url_get;
1.1 millert 777: }
778:
779: /*
780: * Read the rest of the header.
781: */
1.40 fgsch 782: free(buf);
783: filesize = -1;
784:
1.62 ray 785: for (;;) {
1.61 deraadt 786: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 787: warn("Receiving HTTP reply");
788: goto cleanup_url_get;
789: }
1.61 deraadt 790:
1.40 fgsch 791: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
792: buf[--len] = '\0';
793: if (len == 0)
1.1 millert 794: break;
1.80 martynas 795: #ifndef SMALL
1.40 fgsch 796: if (debug)
797: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 798: #endif /* !SMALL */
1.1 millert 799:
1.40 fgsch 800: /* Look for some headers */
801: cp = buf;
1.1 millert 802: #define CONTENTLEN "Content-Length: "
1.40 fgsch 803: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
1.104 sthen 804: size_t s;
1.40 fgsch 805: cp += sizeof(CONTENTLEN) - 1;
1.111 deraadt 806: if ((s = strcspn(cp, " \t")))
1.104 sthen 807: *(cp+s) = 0;
1.58 grunk 808: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
809: if (errstr != NULL)
1.40 fgsch 810: goto improper;
1.75 martynas 811: #ifndef SMALL
1.90 martynas 812: if (restart_point)
1.75 martynas 813: filesize += restart_point;
1.78 martynas 814: #endif /* !SMALL */
1.40 fgsch 815: #define LOCATION "Location: "
816: } else if (isredirect &&
817: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
818: cp += sizeof(LOCATION) - 1;
1.102 halex 819: if (strstr(cp, "://") == NULL) {
820: #ifdef SMALL
821: errx(1, "Relative redirect not supported");
822: #else /* SMALL */
823: if (*cp == '/') {
824: locbase = NULL;
825: cp++;
826: } else {
827: locbase = strdup(path);
828: if (locbase == NULL)
829: errx(1, "Can't allocate memory"
830: " for location base");
831: loctail = strchr(locbase, '#');
832: if (loctail != NULL)
833: *loctail = '\0';
834: loctail = strchr(locbase, '?');
835: if (loctail != NULL)
836: *loctail = '\0';
837: loctail = strrchr(locbase, '/');
838: if (loctail == NULL) {
839: free(locbase);
840: locbase = NULL;
841: } else
842: loctail[1] = '\0';
843: }
844: /* Contruct URL from relative redirect */
845: if (asprintf(&redirurl, "%s%s%s%s/%s%s",
846: scheme, full_host,
847: portnum ? ":" : "",
848: portnum ? portnum : "",
849: locbase ? locbase : "",
850: cp) == -1)
851: errx(1, "Cannot build "
852: "redirect URL");
853: free(locbase);
854: #endif /* SMALL */
855: } else if ((redirurl = strdup(cp)) == NULL)
856: errx(1, "Cannot allocate memory for URL");
857: loctail = strchr(redirurl, '#');
858: if (loctail != NULL)
859: *loctail = '\0';
1.40 fgsch 860: if (verbose)
1.102 halex 861: fprintf(ttyout, "Redirected to %s\n", redirurl);
1.40 fgsch 862: if (fin != NULL)
863: fclose(fin);
864: else if (s != -1)
865: close(s);
1.102 halex 866: rval = url_get(redirurl, proxyenv, savefile);
867: free(redirurl);
868: goto cleanup_url_get;
1.40 fgsch 869: }
1.108 tobias 870: free(buf);
1.1 millert 871: }
872:
1.17 millert 873: /* Open the output file. */
1.100 halex 874: if (!pipeout) {
1.75 martynas 875: #ifndef SMALL
876: if (resume)
1.83 martynas 877: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
878: 0666);
1.75 martynas 879: else
1.78 martynas 880: #endif /* !SMALL */
1.75 martynas 881: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
882: 0666);
1.10 deraadt 883: if (out < 0) {
884: warn("Can't open %s", savefile);
885: goto cleanup_url_get;
886: }
887: } else
1.17 millert 888: out = fileno(stdout);
1.1 millert 889:
890: /* Trap signals */
891: oldintr = NULL;
1.97 martynas 892: oldinti = NULL;
1.1 millert 893: if (setjmp(httpabort)) {
894: if (oldintr)
1.2 millert 895: (void)signal(SIGINT, oldintr);
1.97 martynas 896: if (oldinti)
897: (void)signal(SIGINFO, oldinti);
1.6 millert 898: goto cleanup_url_get;
1.1 millert 899: }
900: oldintr = signal(SIGINT, aborthttp);
901:
902: bytes = 0;
903: hashbytes = mark;
1.84 martynas 904: progressmeter(-1, path);
1.43 millert 905:
906: free(buf);
1.1 millert 907:
908: /* Finally, suck down the file. */
1.113 tedu 909: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 910: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 911: i = 0;
1.61 deraadt 912: len = 1;
1.97 martynas 913: oldinti = signal(SIGINFO, psummary);
1.61 deraadt 914: while (len > 0) {
1.113 tedu 915: len = ftp_read(fin, ssl, buf, buflen);
1.1 millert 916: bytes += len;
1.61 deraadt 917: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
918: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 919: warn("Writing %s", savefile);
1.97 martynas 920: signal(SIGINFO, oldinti);
1.6 millert 921: goto cleanup_url_get;
1.1 millert 922: }
923: else if (i == 0)
924: break;
925: }
926: if (hash && !progress) {
927: while (bytes >= hashbytes) {
1.10 deraadt 928: (void)putc('#', ttyout);
1.1 millert 929: hashbytes += mark;
930: }
1.10 deraadt 931: (void)fflush(ttyout);
1.1 millert 932: }
933: }
1.97 martynas 934: signal(SIGINFO, oldinti);
1.1 millert 935: if (hash && !progress && bytes > 0) {
936: if (bytes < mark)
1.10 deraadt 937: (void)putc('#', ttyout);
938: (void)putc('\n', ttyout);
939: (void)fflush(ttyout);
1.1 millert 940: }
941: if (len != 0) {
942: warn("Reading from socket");
1.6 millert 943: goto cleanup_url_get;
1.1 millert 944: }
1.84 martynas 945: progressmeter(1, NULL);
1.75 martynas 946: if (
947: #ifndef SMALL
948: !resume &&
1.78 martynas 949: #endif /* !SMALL */
1.75 martynas 950: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 951: if (verbose)
952: fputs("Read short file.\n", ttyout);
953: goto cleanup_url_get;
954: }
955:
1.1 millert 956: if (verbose)
1.97 martynas 957: ptransfer(0);
1.2 millert 958: (void)signal(SIGINT, oldintr);
1.1 millert 959:
1.40 fgsch 960: rval = 0;
961: goto cleanup_url_get;
1.1 millert 962:
1.14 millert 963: noftpautologin:
964: warnx(
965: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
966: goto cleanup_url_get;
967:
1.1 millert 968: improper:
1.8 millert 969: warnx("Improper response from %s", host);
1.14 millert 970:
1.6 millert 971: cleanup_url_get:
1.61 deraadt 972: #ifndef SMALL
1.126 jsing 973: if (ssl != NULL) {
974: ressl_close(ssl);
975: ressl_free(ssl);
1.61 deraadt 976: }
1.102 halex 977: free(full_host);
1.78 martynas 978: #endif /* !SMALL */
1.40 fgsch 979: if (fin != NULL)
980: fclose(fin);
981: else if (s != -1)
1.1 millert 982: close(s);
1.67 steven 983: free(buf);
984: free(proxyurl);
1.62 ray 985: free(newline);
1.123 guenther 986: free(credentials);
1.40 fgsch 987: return (rval);
1.1 millert 988: }
989:
990: /*
991: * Abort a http retrieval
992: */
1.51 deraadt 993: /* ARGSUSED */
1.1 millert 994: void
1.51 deraadt 995: aborthttp(int signo)
1.1 millert 996: {
997:
998: alarmtimer(0);
1.10 deraadt 999: fputs("\nhttp fetch aborted.\n", ttyout);
1000: (void)fflush(ttyout);
1.1 millert 1001: longjmp(httpabort, 1);
1002: }
1003:
1004: /*
1.22 deraadt 1005: * Abort a http retrieval
1006: */
1.51 deraadt 1007: /* ARGSUSED */
1.22 deraadt 1008: void
1.51 deraadt 1009: abortfile(int signo)
1.22 deraadt 1010: {
1011:
1012: alarmtimer(0);
1013: fputs("\nfile fetch aborted.\n", ttyout);
1014: (void)fflush(ttyout);
1015: longjmp(httpabort, 1);
1016: }
1017:
1018: /*
1.1 millert 1019: * Retrieve multiple files from the command line, transferring
1020: * files of the form "host:path", "ftp://host/path" using the
1021: * ftp protocol, and files of the form "http://host/path" using
1022: * the http protocol.
1.2 millert 1023: * If path has a trailing "/", then return (-1);
1.1 millert 1024: * the path will be cd-ed into and the connection remains open,
1025: * and the function will return -1 (to indicate the connection
1026: * is alive).
1027: * If an error occurs the return value will be the offset+1 in
1028: * argv[] of the file that caused a problem (i.e, argv[x]
1029: * returns x+1)
1030: * Otherwise, 0 is returned if all files retrieved successfully.
1031: */
1032: int
1.50 deraadt 1033: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 1034: {
1035: char *xargv[5];
1.62 ray 1036: char *cp, *url, *host, *dir, *file, *portnum;
1037: char *username, *pass, *pathstart;
1.6 millert 1038: char *ftpproxy, *httpproxy;
1.14 millert 1039: int rval, xargc;
1040: volatile int argpos;
1.49 krw 1041: int dirhasglob, filehasglob, oautologin;
1.14 millert 1042: char rempath[MAXPATHLEN];
1.1 millert 1043:
1044: argpos = 0;
1045:
1046: if (setjmp(toplevel)) {
1047: if (connected)
1048: disconnect(0, NULL);
1.2 millert 1049: return (argpos + 1);
1.1 millert 1050: }
1.3 millert 1051: (void)signal(SIGINT, (sig_t)intr);
1052: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 1053:
1.45 millert 1054: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
1055: ftpproxy = NULL;
1056: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
1057: httpproxy = NULL;
1.6 millert 1058:
1.1 millert 1059: /*
1060: * Loop through as long as there's files to fetch.
1061: */
1.123 guenther 1062: username = pass = NULL;
1.62 ray 1063: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 1064: if (strchr(argv[argpos], ':') == NULL)
1065: break;
1.123 guenther 1066:
1067: free(username);
1068: free(pass);
1.62 ray 1069: host = dir = file = portnum = username = pass = NULL;
1.1 millert 1070:
1071: /*
1072: * We muck with the string, so we make a copy.
1073: */
1.62 ray 1074: url = strdup(argv[argpos]);
1075: if (url == NULL)
1.1 millert 1076: errx(1, "Can't allocate memory for auto-fetch.");
1077:
1078: /*
1079: * Try HTTP URL-style arguments first.
1080: */
1.62 ray 1081: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1082: #ifndef SMALL
1083: /* even if we compiled without SSL, url_get will check */
1.62 ray 1084: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 1085: #endif /* !SMALL */
1.62 ray 1086: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 1087: redirect_loop = 0;
1.62 ray 1088: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 1089: rval = argpos + 1;
1090: continue;
1091: }
1092:
1093: /*
1.6 millert 1094: * Try FTP URL-style arguments next. If ftpproxy is
1095: * set, use url_get() instead of standard ftp.
1096: * Finally, try host:file.
1.1 millert 1097: */
1.62 ray 1098: host = url;
1099: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 1100: char *passend, *passagain, *userend;
1.31 itojun 1101:
1.6 millert 1102: if (ftpproxy) {
1.62 ray 1103: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 1104: rval = argpos + 1;
1105: continue;
1106: }
1.1 millert 1107: host += sizeof(FTP_URL) - 1;
1.8 millert 1108: dir = strchr(host, '/');
1.1 millert 1109:
1.8 millert 1110: /* Look for [user:pass@]host[:port] */
1.31 itojun 1111:
1112: /* check if we have "user:pass@" */
1.37 heko 1113: userend = strchr(host, ':');
1.31 itojun 1114: passend = strchr(host, '@');
1115: if (passend && userend && userend < passend &&
1116: (!dir || passend < dir)) {
1.62 ray 1117: username = host;
1.31 itojun 1118: pass = userend + 1;
1119: host = passend + 1;
1120: *userend = *passend = '\0';
1.37 heko 1121: passagain = strchr(host, '@');
1.42 deraadt 1122: if (strchr(pass, '@') != NULL ||
1.37 heko 1123: (passagain != NULL && passagain < dir)) {
1124: warnx(at_encoding_warning);
1.123 guenther 1125: username = pass = NULL;
1.37 heko 1126: goto bad_ftp_url;
1.42 deraadt 1127: }
1.31 itojun 1128:
1.77 martynas 1129: if (EMPTYSTRING(username)) {
1.11 millert 1130: bad_ftp_url:
1.31 itojun 1131: warnx("Invalid URL: %s", argv[argpos]);
1132: rval = argpos + 1;
1.123 guenther 1133: username = pass = NULL;
1.31 itojun 1134: continue;
1135: }
1.62 ray 1136: username = urldecode(username);
1.37 heko 1137: pass = urldecode(pass);
1.8 millert 1138: }
1.31 itojun 1139:
1140: #ifdef INET6
1141: /* check [host]:port, or [host] */
1142: if (host[0] == '[') {
1143: cp = strchr(host, ']');
1144: if (cp && (!dir || cp < dir)) {
1145: if (cp + 1 == dir || cp[1] == ':') {
1146: host++;
1147: *cp++ = '\0';
1148: } else
1149: cp = NULL;
1150: } else
1151: cp = host;
1.25 itojun 1152: } else
1153: cp = host;
1.31 itojun 1154: #else
1155: cp = host;
1.25 itojun 1156: #endif
1.31 itojun 1157:
1158: /* split off host[:port] if there is */
1159: if (cp) {
1160: portnum = strchr(cp, ':');
1.52 henning 1161: pathstart = strchr(cp, '/');
1162: /* : in path is not a port # indicator */
1163: if (portnum && pathstart &&
1164: pathstart < portnum)
1165: portnum = NULL;
1166:
1.31 itojun 1167: if (!portnum)
1168: ;
1169: else {
1170: if (!dir)
1171: ;
1172: else if (portnum + 1 < dir) {
1173: *portnum++ = '\0';
1174: /*
1175: * XXX should check if portnum
1176: * is decimal number
1177: */
1178: } else {
1179: /* empty portnum */
1180: goto bad_ftp_url;
1181: }
1182: }
1183: } else
1184: portnum = NULL;
1.8 millert 1185: } else { /* classic style `host:file' */
1186: dir = strchr(host, ':');
1187: }
1.1 millert 1188: if (EMPTYSTRING(host)) {
1189: rval = argpos + 1;
1190: continue;
1191: }
1192:
1193: /*
1.9 millert 1194: * If dir is NULL, the file wasn't specified
1.1 millert 1195: * (URL looked something like ftp://host)
1196: */
1.8 millert 1197: if (dir != NULL)
1198: *dir++ = '\0';
1.1 millert 1199:
1200: /*
1201: * Extract the file and (if present) directory name.
1202: */
1.42 deraadt 1203: if (!EMPTYSTRING(dir)) {
1.8 millert 1204: cp = strrchr(dir, '/');
1.1 millert 1205: if (cp != NULL) {
1206: *cp++ = '\0';
1207: file = cp;
1208: } else {
1209: file = dir;
1210: dir = NULL;
1211: }
1212: }
1.80 martynas 1213: #ifndef SMALL
1.1 millert 1214: if (debug)
1.42 deraadt 1215: fprintf(ttyout,
1216: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1217: username, pass ? "XXXX" : NULL, host, portnum,
1218: dir, file);
1.80 martynas 1219: #endif /* !SMALL */
1.1 millert 1220:
1221: /*
1.49 krw 1222: * Set up the connection.
1.1 millert 1223: */
1.49 krw 1224: if (connected)
1225: disconnect(0, NULL);
1226: xargv[0] = __progname;
1227: xargv[1] = host;
1.8 millert 1228: xargv[2] = NULL;
1.49 krw 1229: xargc = 2;
1230: if (!EMPTYSTRING(portnum)) {
1231: xargv[2] = portnum;
1232: xargv[3] = NULL;
1233: xargc = 3;
1234: }
1235: oautologin = autologin;
1.88 martynas 1236: if (username == NULL)
1.89 halex 1237: anonftp = 1;
1238: else {
1239: anonftp = 0;
1.49 krw 1240: autologin = 0;
1.89 halex 1241: }
1.49 krw 1242: setpeer(xargc, xargv);
1243: autologin = oautologin;
1.87 martynas 1244: if (connected == 0 ||
1245: (connected == 1 && autologin && (username == NULL ||
1246: !ftp_login(host, username, pass)))) {
1.49 krw 1247: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1248: rval = argpos + 1;
1249: continue;
1.1 millert 1250: }
1.49 krw 1251:
1252: /* Always use binary transfers. */
1253: setbinary(0, NULL);
1.1 millert 1254:
1.4 millert 1255: dirhasglob = filehasglob = 0;
1256: if (doglob) {
1.42 deraadt 1257: if (!EMPTYSTRING(dir) &&
1.4 millert 1258: strpbrk(dir, "*?[]{}") != NULL)
1259: dirhasglob = 1;
1.42 deraadt 1260: if (!EMPTYSTRING(file) &&
1.4 millert 1261: strpbrk(file, "*?[]{}") != NULL)
1262: filehasglob = 1;
1263: }
1264:
1.1 millert 1265: /* Change directories, if necessary. */
1.42 deraadt 1266: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1267: xargv[0] = "cd";
1268: xargv[1] = dir;
1269: xargv[2] = NULL;
1270: cd(2, xargv);
1.42 deraadt 1271: if (!dirchange) {
1.1 millert 1272: rval = argpos + 1;
1273: continue;
1274: }
1275: }
1276:
1277: if (EMPTYSTRING(file)) {
1.86 martynas 1278: #ifndef SMALL
1.1 millert 1279: rval = -1;
1.86 martynas 1280: #else /* !SMALL */
1281: recvrequest("NLST", "-", NULL, "w", 0, 0);
1282: rval = 0;
1283: #endif /* !SMALL */
1.1 millert 1284: continue;
1285: }
1286:
1.21 marc 1287: if (verbose)
1.10 deraadt 1288: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1289:
1.4 millert 1290: if (dirhasglob) {
1291: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1292: file = rempath;
1293: }
1294:
1295: /* Fetch the file(s). */
1.10 deraadt 1296: xargc = 2;
1.1 millert 1297: xargv[0] = "get";
1298: xargv[1] = file;
1299: xargv[2] = NULL;
1.4 millert 1300: if (dirhasglob || filehasglob) {
1301: int ointeractive;
1302:
1303: ointeractive = interactive;
1304: interactive = 0;
1305: xargv[0] = "mget";
1.78 martynas 1306: #ifndef SMALL
1307: if (resume) {
1308: xargc = 3;
1309: xargv[1] = "-c";
1310: xargv[2] = file;
1311: xargv[3] = NULL;
1312: }
1313: #endif /* !SMALL */
1.10 deraadt 1314: mget(xargc, xargv);
1.5 millert 1315: interactive = ointeractive;
1.10 deraadt 1316: } else {
1.17 millert 1317: if (outfile != NULL) {
1318: xargv[2] = outfile;
1319: xargv[3] = NULL;
1.10 deraadt 1320: xargc++;
1321: }
1.75 martynas 1322: #ifndef SMALL
1323: if (resume)
1324: reget(xargc, xargv);
1325: else
1.78 martynas 1326: #endif /* !SMALL */
1.75 martynas 1327: get(xargc, xargv);
1.10 deraadt 1328: }
1.1 millert 1329:
1.4 millert 1330: if ((code / 100) != COMPLETE)
1.1 millert 1331: rval = argpos + 1;
1332: }
1333: if (connected && rval != -1)
1334: disconnect(0, NULL);
1335: return (rval);
1.37 heko 1336: }
1337:
1338: char *
1.50 deraadt 1339: urldecode(const char *str)
1.37 heko 1340: {
1.53 deraadt 1341: char *ret, c;
1342: int i, reallen;
1.37 heko 1343:
1.53 deraadt 1344: if (str == NULL)
1345: return NULL;
1346: if ((ret = malloc(strlen(str)+1)) == NULL)
1347: err(1, "Can't allocate memory for URL decoding");
1348: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1349: c = str[i];
1350: if (c == '+') {
1351: *ret = ' ';
1352: continue;
1353: }
1.61 deraadt 1354:
1355: /* Cannot use strtol here because next char
1356: * after %xx may be a digit.
1357: */
1.53 deraadt 1358: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1359: *ret = hextochar(&str[i+1]);
1360: i+=2;
1361: continue;
1362: }
1363: *ret = c;
1364: }
1365: *ret = '\0';
1366:
1367: return ret-reallen;
1.123 guenther 1368: }
1369:
1370: char *
1371: recode_credentials(const char *userinfo)
1372: {
1373: char *ui, *creds;
1374: size_t ulen, credsize;
1375:
1376: /* url-decode the user and pass */
1377: ui = urldecode(userinfo);
1378:
1379: ulen = strlen(ui);
1380: credsize = (ulen + 2) / 3 * 4 + 1;
1381: creds = malloc(credsize);
1382: if (creds == NULL)
1383: errx(1, "out of memory");
1384: if (b64_ntop(ui, ulen, creds, credsize) == -1)
1385: errx(1, "error in base64 encoding");
1386: free(ui);
1387: return (creds);
1.37 heko 1388: }
1389:
1390: char
1.50 deraadt 1391: hextochar(const char *str)
1.37 heko 1392: {
1.53 deraadt 1393: char c, ret;
1.37 heko 1394:
1.53 deraadt 1395: c = str[0];
1396: ret = c;
1397: if (isalpha(c))
1398: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1399: else
1400: ret -= '0';
1401: ret *= 16;
1402:
1403: c = str[1];
1404: ret += c;
1405: if (isalpha(c))
1406: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1407: else
1408: ret -= '0';
1409: return ret;
1.25 itojun 1410: }
1411:
1412: int
1.50 deraadt 1413: isurl(const char *p)
1.25 itojun 1414: {
1.27 millert 1415:
1.26 deraadt 1416: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1417: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1418: #ifndef SMALL
1419: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1420: #endif /* !SMALL */
1.27 millert 1421: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1422: strstr(p, ":/"))
1423: return (1);
1424: return (0);
1.1 millert 1425: }
1.61 deraadt 1426:
1427: char *
1.126 jsing 1428: ftp_readline(FILE *fp, struct ressl *ssl, size_t *lenp)
1.61 deraadt 1429: {
1430: if (fp != NULL)
1431: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1432: #ifndef SMALL
1433: else if (ssl != NULL)
1434: return SSL_readline(ssl, lenp);
1.78 martynas 1435: #endif /* !SMALL */
1.61 deraadt 1436: else
1437: return NULL;
1438: }
1439:
1.65 ray 1440: size_t
1.126 jsing 1441: ftp_read(FILE *fp, struct ressl *ssl, char *buf, size_t len)
1.61 deraadt 1442: {
1.65 ray 1443: size_t ret;
1.61 deraadt 1444: if (fp != NULL)
1445: ret = fread(buf, sizeof(char), len, fp);
1446: #ifndef SMALL
1.65 ray 1447: else if (ssl != NULL) {
1.126 jsing 1448: size_t nr;
1.65 ray 1449:
1.126 jsing 1450: if ((ret = ressl_read(ssl, buf, len, &nr)) != 0)
1.65 ray 1451: ret = 0;
1452: else
1453: ret = nr;
1454: }
1.78 martynas 1455: #endif /* !SMALL */
1.61 deraadt 1456: else
1457: ret = 0;
1458: return (ret);
1459: }
1460:
1461: int
1.126 jsing 1462: ftp_printf(FILE *fp, struct ressl *ssl, const char *fmt, ...)
1.61 deraadt 1463: {
1464: int ret;
1465: va_list ap;
1466:
1467: va_start(ap, fmt);
1468:
1469: if (fp != NULL)
1470: ret = vfprintf(fp, fmt, ap);
1471: #ifndef SMALL
1472: else if (ssl != NULL)
1.126 jsing 1473: ret = SSL_vprintf(ssl, fmt, ap);
1.78 martynas 1474: #endif /* !SMALL */
1.61 deraadt 1475: else
1.92 jsg 1476: ret = 0;
1.61 deraadt 1477:
1478: va_end(ap);
1479: return (ret);
1480: }
1481:
1482: #ifndef SMALL
1483: int
1.126 jsing 1484: SSL_vprintf(struct ressl *ssl, const char *fmt, va_list ap)
1.61 deraadt 1485: {
1.126 jsing 1486: char *string;
1487: size_t nw;
1.61 deraadt 1488: int ret;
1489:
1490: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1491: return ret;
1.126 jsing 1492: ret = ressl_write(ssl, string, ret, &nw);
1.61 deraadt 1493: free(string);
1494: return ret;
1495: }
1496:
1497: char *
1.126 jsing 1498: SSL_readline(struct ressl *ssl, size_t *lenp)
1.61 deraadt 1499: {
1.126 jsing 1500: size_t i, len, nr;
1.61 deraadt 1501: char *buf, *q, c;
1.109 sthen 1502: int ret;
1.61 deraadt 1503:
1504: len = 128;
1505: if ((buf = malloc(len)) == NULL)
1506: errx(1, "Can't allocate memory for transfer buffer");
1507: for (i = 0; ; i++) {
1508: if (i >= len - 1) {
1509: if ((q = realloc(buf, 2 * len)) == NULL)
1510: errx(1, "Can't expand transfer buffer");
1511: buf = q;
1512: len *= 2;
1513: }
1.109 sthen 1514: again:
1.126 jsing 1515: ret = ressl_read(ssl, &c, 1, &nr);
1516: if (ret == -2)
1517: goto again;
1518: if (ret != 0)
1519: errx(1, "SSL read error: %u", ret);
1520:
1.61 deraadt 1521: buf[i] = c;
1522: if (c == '\n')
1523: break;
1524: }
1525: *lenp = i;
1526: return (buf);
1527: }
1528:
1529: int
1.81 espie 1530: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1531: {
1.66 ray 1532: int l;
1.61 deraadt 1533: char buf[1024];
1534: char *connstr, *hosttail, *port;
1535:
1536: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1537: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1538: host++;
1539: *hosttail++ = '\0';
1540: } else
1541: hosttail = host;
1542:
1543: port = strrchr(hosttail, ':'); /* find portnum */
1544: if (port != NULL)
1545: *port++ = '\0';
1546: if (!port)
1547: port = "443";
1548:
1.81 espie 1549: if (cookie) {
1550: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1551: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1552: host, port, cookie, HTTP_USER_AGENT);
1553: } else {
1554: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1555: host, port, HTTP_USER_AGENT);
1556: }
1557:
1.66 ray 1558: if (l == -1)
1.61 deraadt 1559: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1560: #ifndef SMALL
1.68 ray 1561: if (debug)
1562: printf("%s", connstr);
1.80 martynas 1563: #endif /* !SMALL */
1.66 ray 1564: if (write(socket, connstr, l) != l)
1.68 ray 1565: err(1, "Could not send connect string");
1.61 deraadt 1566: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1567: free(connstr);
1.61 deraadt 1568: return(200);
1569: }
1.78 martynas 1570: #endif /* !SMALL */