Annotation of src/usr.bin/ftp/fetch.c, Revision 1.94
1.94 ! martynas 1: /* $OpenBSD: fetch.c,v 1.93 2009/07/27 23:11:26 martynas Exp $ */
1.15 millert 2: /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */
1.1 millert 3:
4: /*-
5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Jason Thorpe and Luke Mewburn.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.15 millert 23: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 millert 25: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30: * POSSIBILITY OF SUCH DAMAGE.
31: */
32:
33: /*
34: * FTP User Program -- Command line file retrieval
35: */
36:
37: #include <sys/types.h>
38: #include <sys/param.h>
39: #include <sys/socket.h>
1.22 deraadt 40: #include <sys/stat.h>
1.1 millert 41:
42: #include <netinet/in.h>
43:
44: #include <arpa/ftp.h>
45: #include <arpa/inet.h>
46:
47: #include <ctype.h>
48: #include <err.h>
1.17 millert 49: #include <libgen.h>
1.58 grunk 50: #include <limits.h>
1.1 millert 51: #include <netdb.h>
52: #include <fcntl.h>
1.3 millert 53: #include <signal.h>
1.1 millert 54: #include <stdio.h>
1.61 deraadt 55: #include <stdarg.h>
1.19 deraadt 56: #include <errno.h>
1.1 millert 57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
1.40 fgsch 60: #include <util.h>
1.73 drahn 61: #include <resolv.h>
1.1 millert 62:
1.61 deraadt 63: #ifndef SMALL
64: #include <openssl/ssl.h>
65: #include <openssl/err.h>
1.78 martynas 66: #else /* !SMALL */
1.61 deraadt 67: #define SSL void
1.78 martynas 68: #endif /* !SMALL */
1.61 deraadt 69:
1.1 millert 70: #include "ftp_var.h"
1.86 martynas 71: #include "cmds.h"
1.1 millert 72:
1.38 millert 73: static int url_get(const char *, const char *, const char *);
74: void aborthttp(int);
75: void abortfile(int);
1.42 deraadt 76: char hextochar(const char *);
77: char *urldecode(const char *);
1.61 deraadt 78: int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4)));
79: char *ftp_readline(FILE *, SSL *, size_t *);
1.65 ray 80: size_t ftp_read(FILE *, SSL *, char *, size_t);
1.61 deraadt 81: #ifndef SMALL
1.81 espie 82: int proxy_connect(int, char *, char *);
1.61 deraadt 83: int SSL_vprintf(SSL *, const char *, va_list);
84: char *SSL_readline(SSL *, 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:
1.73 drahn 94: #define COOKIE_MAX_LEN 42
1.1 millert 95:
96: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
97:
1.37 heko 98: static const char *at_encoding_warning =
1.53 deraadt 99: "Extra `@' characters in usernames and passwords should be encoded as %%40";
1.37 heko 100:
1.1 millert 101: jmp_buf httpabort;
102:
1.54 fgsch 103: static int redirect_loop;
104:
1.1 millert 105: /*
1.6 millert 106: * Retrieve URL, via the proxy in $proxyvar if necessary.
1.1 millert 107: * Modifies the string argument given.
108: * Returns -1 on failure, 0 on success
109: */
1.14 millert 110: static int
1.50 deraadt 111: url_get(const char *origline, const char *proxyenv, const char *outfile)
1.1 millert 112: {
1.69 jsg 113: char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4];
1.62 ray 114: char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
1.53 deraadt 115: int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
1.25 itojun 116: struct addrinfo hints, *res0, *res;
1.34 millert 117: const char * volatile savefile;
1.62 ray 118: char * volatile proxyurl = NULL;
1.73 drahn 119: char *cookie = NULL;
1.53 deraadt 120: volatile int s = -1, out;
1.14 millert 121: volatile sig_t oldintr;
1.53 deraadt 122: FILE *fin = NULL;
1.1 millert 123: off_t hashbytes;
1.58 grunk 124: const char *errstr;
1.61 deraadt 125: size_t len, wlen;
126: #ifndef SMALL
127: char *sslpath = NULL, *sslhost = NULL;
1.70 deraadt 128: int ishttpsurl = 0;
1.61 deraadt 129: SSL_CTX *ssl_ctx = NULL;
1.78 martynas 130: #endif /* !SMALL */
1.61 deraadt 131: SSL *ssl = NULL;
1.70 deraadt 132: int status;
1.14 millert 133:
1.62 ray 134: newline = strdup(origline);
135: if (newline == NULL)
1.14 millert 136: errx(1, "Can't allocate memory to parse URL");
1.62 ray 137: if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
138: host = newline + sizeof(HTTP_URL) - 1;
139: else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
140: host = newline + sizeof(FTP_URL) - 1;
1.14 millert 141: isftpurl = 1;
1.62 ray 142: } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
143: host = newline + sizeof(FILE_URL) - 1;
1.22 deraadt 144: isfileurl = 1;
1.61 deraadt 145: #ifndef SMALL
1.62 ray 146: } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) {
147: host = newline + sizeof(HTTPS_URL) - 1;
1.61 deraadt 148: ishttpsurl = 1;
1.78 martynas 149: #endif /* !SMALL */
1.14 millert 150: } else
1.62 ray 151: errx(1, "url_get: Invalid URL '%s'", newline);
1.6 millert 152:
1.22 deraadt 153: if (isfileurl) {
154: path = host;
155: } else {
1.93 martynas 156: path = strchr(host, '/'); /* Find path */
1.22 deraadt 157: if (EMPTYSTRING(path)) {
1.93 martynas 158: if (outfile) { /* No slash, but */
159: path=strchr(host,'\0'); /* we have outfile. */
160: goto noslash;
161: }
1.22 deraadt 162: if (isftpurl)
163: goto noftpautologin;
1.94 ! martynas 164: warnx("No `/' after host (use -o): %s", origline);
1.22 deraadt 165: goto cleanup_url_get;
166: }
167: *path++ = '\0';
1.93 martynas 168: if (EMPTYSTRING(path) && !outfile) {
1.22 deraadt 169: if (isftpurl)
170: goto noftpautologin;
1.94 ! martynas 171: warnx("No filename after host (use -o): %s", origline);
1.22 deraadt 172: goto cleanup_url_get;
173: }
1.14 millert 174: }
1.1 millert 175:
1.93 martynas 176: noslash:
1.17 millert 177: if (outfile)
178: savefile = outfile;
1.93 martynas 179: else {
180: if (path[strlen(path) - 1] == '/') /* Consider no file */
181: savefile = NULL; /* after dir invalid. */
182: else
183: savefile = basename(path);
1.75 martynas 184: }
185:
1.14 millert 186: if (EMPTYSTRING(savefile)) {
187: if (isftpurl)
188: goto noftpautologin;
1.94 ! martynas 189: warnx("No filename after directory (use -o): %s", origline);
1.6 millert 190: goto cleanup_url_get;
1.14 millert 191: }
1.1 millert 192:
1.93 martynas 193: #ifndef SMALL
194: if (resume && (strcmp(savefile, "-") == 0)) {
195: warnx("can't append to stdout");
196: goto cleanup_url_get;
197: }
198: #endif /* !SMALL */
199:
1.59 uwe 200: if (!isfileurl && proxyenv != NULL) { /* use proxy */
1.61 deraadt 201: #ifndef SMALL
202: if (ishttpsurl) {
203: sslpath = strdup(path);
204: sslhost = strdup(host);
205: if (! sslpath || ! sslhost)
206: errx(1, "Can't allocate memory for https path/host.");
207: }
1.78 martynas 208: #endif /* !SMALL */
1.62 ray 209: proxyurl = strdup(proxyenv);
210: if (proxyurl == NULL)
1.14 millert 211: errx(1, "Can't allocate memory for proxy URL.");
1.62 ray 212: if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
213: host = proxyurl + sizeof(HTTP_URL) - 1;
214: else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0)
215: host = proxyurl + sizeof(FTP_URL) - 1;
1.6 millert 216: else {
1.14 millert 217: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 218: goto cleanup_url_get;
219: }
1.14 millert 220: if (EMPTYSTRING(host)) {
221: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 222: goto cleanup_url_get;
1.14 millert 223: }
1.93 martynas 224: if (*--path == '\0')
225: *path = '/'; /* add / back to real path */
1.1 millert 226: path = strchr(host, '/'); /* remove trailing / on host */
1.42 deraadt 227: if (!EMPTYSTRING(path))
1.73 drahn 228: *path++ = '\0'; /* i guess this ++ is useless */
229:
230: path = strchr(host, '@'); /* look for credentials in proxy */
231: if (!EMPTYSTRING(path)) {
1.81 espie 232: *path = '\0';
1.73 drahn 233: cookie = strchr(host, ':');
234: if (EMPTYSTRING(cookie)) {
235: warnx("Malformed proxy URL: %s", proxyenv);
236: goto cleanup_url_get;
237: }
238: cookie = malloc(COOKIE_MAX_LEN);
1.81 espie 239: if (cookie == NULL)
240: errx(1, "out of memory");
241: if (b64_ntop(host, strlen(host), cookie, COOKIE_MAX_LEN) == -1)
242: errx(1, "error in base64 encoding");
243: *path = '@'; /* restore @ in proxyurl */
1.73 drahn 244: /*
1.81 espie 245: * This removes the password from proxyurl,
1.73 drahn 246: * filling with stars
247: */
1.81 espie 248: for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@';
1.73 drahn 249: host++)
250: *host = '*';
251:
1.81 espie 252: host = path + 1;
1.73 drahn 253: }
1.62 ray 254: path = newline;
1.1 millert 255: }
256:
1.22 deraadt 257: if (isfileurl) {
258: struct stat st;
259:
260: s = open(path, O_RDONLY);
261: if (s == -1) {
262: warn("Can't open file %s", path);
263: goto cleanup_url_get;
264: }
265:
266: if (fstat(s, &st) == -1)
267: filesize = -1;
268: else
269: filesize = st.st_size;
270:
1.83 martynas 271: /* Open the output file. */
1.22 deraadt 272: if (strcmp(savefile, "-") != 0) {
1.75 martynas 273: #ifndef SMALL
274: if (resume)
1.82 deraadt 275: out = open(savefile, O_CREAT | O_WRONLY |
276: O_APPEND, 0666);
277:
1.75 martynas 278: else
1.78 martynas 279: #endif /* !SMALL */
1.75 martynas 280: out = open(savefile, O_CREAT | O_WRONLY |
281: O_TRUNC, 0666);
1.22 deraadt 282: if (out < 0) {
283: warn("Can't open %s", savefile);
284: goto cleanup_url_get;
285: }
286: } else
287: out = fileno(stdout);
288:
1.75 martynas 289: #ifndef SMALL
290: if (resume) {
291: if (fstat(out, &st) == -1) {
292: warn("Can't fstat %s", savefile);
293: goto cleanup_url_get;
294: }
295: if (lseek(s, st.st_size, SEEK_SET) == -1) {
296: warn("Can't lseek %s", path);
297: goto cleanup_url_get;
298: }
299: restart_point = st.st_size;
300: }
1.78 martynas 301: #endif /* !SMALL */
1.75 martynas 302:
1.22 deraadt 303: /* Trap signals */
304: oldintr = NULL;
305: if (setjmp(httpabort)) {
306: if (oldintr)
307: (void)signal(SIGINT, oldintr);
308: goto cleanup_url_get;
309: }
310: oldintr = signal(SIGINT, abortfile);
1.42 deraadt 311:
1.22 deraadt 312: bytes = 0;
313: hashbytes = mark;
1.84 martynas 314: progressmeter(-1, path);
1.40 fgsch 315:
316: if ((buf = malloc(4096)) == NULL)
1.47 deraadt 317: errx(1, "Can't allocate memory for transfer buffer");
1.42 deraadt 318:
1.22 deraadt 319: /* Finally, suck down the file. */
320: i = 0;
1.40 fgsch 321: while ((len = read(s, buf, 4096)) > 0) {
1.22 deraadt 322: bytes += len;
323: for (cp = buf; len > 0; len -= i, cp += i) {
324: if ((i = write(out, cp, len)) == -1) {
325: warn("Writing %s", savefile);
326: goto cleanup_url_get;
327: }
328: else if (i == 0)
329: break;
330: }
331: if (hash && !progress) {
332: while (bytes >= hashbytes) {
333: (void)putc('#', ttyout);
334: hashbytes += mark;
335: }
336: (void)fflush(ttyout);
337: }
338: }
339: if (hash && !progress && bytes > 0) {
340: if (bytes < mark)
341: (void)putc('#', ttyout);
342: (void)putc('\n', ttyout);
343: (void)fflush(ttyout);
344: }
345: if (len != 0) {
346: warn("Reading from file");
347: goto cleanup_url_get;
348: }
1.84 martynas 349: progressmeter(1, NULL);
1.22 deraadt 350: if (verbose)
351: fputs("Successfully retrieved file.\n", ttyout);
352: (void)signal(SIGINT, oldintr);
1.42 deraadt 353:
1.40 fgsch 354: rval = 0;
355: goto cleanup_url_get;
1.22 deraadt 356: }
357:
1.28 itojun 358: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
359: (hosttail[1] == '\0' || hosttail[1] == ':')) {
360: host++;
361: *hosttail++ = '\0';
362: } else
363: hosttail = host;
364:
365: portnum = strrchr(hosttail, ':'); /* find portnum */
1.1 millert 366: if (portnum != NULL)
367: *portnum++ = '\0';
368:
1.80 martynas 369: #ifndef SMALL
1.1 millert 370: if (debug)
1.10 deraadt 371: fprintf(ttyout, "host %s, port %s, path %s, save as %s.\n",
1.1 millert 372: host, portnum, path, savefile);
1.80 martynas 373: #endif /* !SMALL */
1.1 millert 374:
1.25 itojun 375: memset(&hints, 0, sizeof(hints));
1.39 deraadt 376: hints.ai_family = family;
1.25 itojun 377: hints.ai_socktype = SOCK_STREAM;
1.61 deraadt 378: #ifndef SMALL
379: port = portnum ? portnum : (ishttpsurl ? httpsport : httpport);
1.78 martynas 380: #else /* !SMALL */
1.25 itojun 381: port = portnum ? portnum : httpport;
1.78 martynas 382: #endif /* !SMALL */
1.25 itojun 383: error = getaddrinfo(host, port, &hints, &res0);
1.61 deraadt 384: /*
385: * If the services file is corrupt/missing, fall back
386: * on our hard-coded defines.
387: */
1.30 deraadt 388: if (error == EAI_SERVICE && port == httpport) {
389: snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT);
390: error = getaddrinfo(host, pbuf, &hints, &res0);
1.61 deraadt 391: #ifndef SMALL
392: } else if (error == EAI_SERVICE && port == httpsport) {
393: snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT);
394: error = getaddrinfo(host, pbuf, &hints, &res0);
1.78 martynas 395: #endif /* !SMALL */
1.30 deraadt 396: }
1.25 itojun 397: if (error) {
398: warnx("%s: %s", gai_strerror(error), host);
399: goto cleanup_url_get;
1.1 millert 400: }
401:
1.25 itojun 402: s = -1;
403: for (res = res0; res; res = res->ai_next) {
1.44 itojun 404: if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
405: sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
406: strlcpy(hbuf, "(unknown)", sizeof(hbuf));
1.41 deraadt 407: if (verbose)
1.44 itojun 408: fprintf(ttyout, "Trying %s...\n", hbuf);
1.14 millert 409:
1.25 itojun 410: s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
411: if (s == -1) {
412: cause = "socket";
413: continue;
1.1 millert 414: }
415:
1.25 itojun 416: again:
417: if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
1.57 otto 418: int save_errno;
419:
1.25 itojun 420: if (errno == EINTR)
421: goto again;
1.57 otto 422: save_errno = errno;
1.25 itojun 423: close(s);
1.57 otto 424: errno = save_errno;
1.25 itojun 425: s = -1;
426: cause = "connect";
1.19 deraadt 427: continue;
428: }
1.25 itojun 429:
1.29 itojun 430: /* get port in numeric */
431: if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0,
432: pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0)
433: port = pbuf;
434: else
435: port = NULL;
436:
1.61 deraadt 437: #ifndef SMALL
438: if (proxyenv && sslhost)
1.81 espie 439: proxy_connect(s, sslhost, cookie);
1.78 martynas 440: #endif /* !SMALL */
1.25 itojun 441: break;
442: }
443: freeaddrinfo(res0);
444: if (s < 0) {
1.33 millert 445: warn("%s", cause);
1.6 millert 446: goto cleanup_url_get;
1.1 millert 447: }
448:
1.61 deraadt 449: #ifndef SMALL
450: if (ishttpsurl) {
451: if (proxyenv && sslpath) {
452: ishttpsurl = 0;
1.62 ray 453: proxyurl = NULL;
1.61 deraadt 454: path = sslpath;
455: }
456: SSL_library_init();
457: SSL_load_error_strings();
458: SSLeay_add_ssl_algorithms();
459: ssl_ctx = SSL_CTX_new(SSLv23_client_method());
460: ssl = SSL_new(ssl_ctx);
461: if (ssl == NULL || ssl_ctx == NULL) {
462: ERR_print_errors_fp(ttyout);
463: goto cleanup_url_get;
464: }
465: if (SSL_set_fd(ssl, s) == 0) {
466: ERR_print_errors_fp(ttyout);
467: goto cleanup_url_get;
468: }
469: if (SSL_connect(ssl) <= 0) {
470: ERR_print_errors_fp(ttyout);
1.72 ray 471: goto cleanup_url_get;
1.61 deraadt 472: }
473: } else {
474: fin = fdopen(s, "r+");
475: }
1.78 martynas 476: #else /* !SMALL */
1.40 fgsch 477: fin = fdopen(s, "r+");
1.78 martynas 478: #endif /* !SMALL */
1.40 fgsch 479:
1.55 fgsch 480: if (verbose)
481: fprintf(ttyout, "Requesting %s", origline);
1.1 millert 482: /*
1.40 fgsch 483: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 484: */
1.74 pyr 485: #ifndef SMALL
486: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 487: #endif /* !SMALL */
1.62 ray 488: if (proxyurl) {
1.55 fgsch 489: if (verbose)
1.81 espie 490: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 491: /*
492: * Host: directive must use the destination host address for
493: * the original URI (path). We do not attach it at this moment.
494: */
1.73 drahn 495: if (cookie)
496: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 497: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
498: path, cookie, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 499: else
1.74 pyr 500: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
501: path, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 502:
1.28 itojun 503: } else {
1.90 martynas 504: #ifndef SMALL
505: if (resume) {
506: struct stat stbuf;
507:
508: if (stat(savefile, &stbuf) == 0)
509: restart_point = stbuf.st_size;
510: else
511: restart_point = 0;
512: }
513: #endif /* !SMALL */
1.75 martynas 514: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", path,
515: #ifndef SMALL
1.90 martynas 516: restart_point ? "HTTP/1.1" :
1.78 martynas 517: #endif /* !SMALL */
1.75 martynas 518: "HTTP/1.0");
1.32 itojun 519: if (strchr(host, ':')) {
520: char *h, *p;
521:
1.55 fgsch 522: /*
523: * strip off scoped address portion, since it's
524: * local to node
525: */
1.32 itojun 526: h = strdup(host);
527: if (h == NULL)
528: errx(1, "Can't allocate memory.");
529: if ((p = strchr(h, '%')) != NULL)
530: *p = '\0';
1.61 deraadt 531: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 532: free(h);
1.55 fgsch 533: } else
1.61 deraadt 534: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 535:
536: /*
537: * Send port number only if it's specified and does not equal
538: * 80. Some broken HTTP servers get confused if you explicitly
539: * send them the port number.
540: */
1.61 deraadt 541: #ifndef SMALL
542: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
543: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 544: if (restart_point)
1.75 martynas 545: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
546: (long long)restart_point);
1.78 martynas 547: #else /* !SMALL */
1.55 fgsch 548: if (port && strcmp(port, "80") != 0)
1.61 deraadt 549: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 550: #endif /* !SMALL */
1.74 pyr 551: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
552: buf ? buf : "", HTTP_USER_AGENT);
1.55 fgsch 553: if (verbose)
554: fprintf(ttyout, "\n");
1.28 itojun 555: }
1.74 pyr 556:
557:
558: #ifndef SMALL
559: free(buf);
1.78 martynas 560: #endif /* !SMALL */
1.74 pyr 561: buf = NULL;
562:
1.61 deraadt 563: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 564: warn("Writing HTTP request");
1.6 millert 565: goto cleanup_url_get;
1.1 millert 566: }
1.61 deraadt 567: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 568: warn("Receiving HTTP reply");
569: goto cleanup_url_get;
1.1 millert 570: }
1.40 fgsch 571:
572: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
573: buf[--len] = '\0';
1.80 martynas 574: #ifndef SMALL
1.40 fgsch 575: if (debug)
576: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 577: #endif /* !SMALL */
1.40 fgsch 578:
1.1 millert 579: cp = strchr(buf, ' ');
580: if (cp == NULL)
581: goto improper;
582: else
583: cp++;
1.69 jsg 584:
585: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 586: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 587: if (errstr) {
588: warnx("Error retrieving file: %s", cp);
589: goto cleanup_url_get;
590: }
591:
592: switch (status) {
593: case 200: /* OK */
1.75 martynas 594: #ifndef SMALL
595: case 206: /* Partial Content */
1.91 halex 596: #endif /* !SMALL */
1.69 jsg 597: break;
598: case 301: /* Moved Permanently */
599: case 302: /* Found */
600: case 303: /* See Other */
601: case 307: /* Temporary Redirect */
1.40 fgsch 602: isredirect++;
1.54 fgsch 603: if (redirect_loop++ > 10) {
604: warnx("Too many redirections requested");
605: goto cleanup_url_get;
606: }
1.69 jsg 607: break;
1.75 martynas 608: #ifndef SMALL
609: case 416: /* Requested Range Not Satisfiable */
610: warnx("File is already fully retrieved.");
611: goto cleanup_url_get;
1.78 martynas 612: #endif /* !SMALL */
1.69 jsg 613: default:
1.1 millert 614: warnx("Error retrieving file: %s", cp);
1.6 millert 615: goto cleanup_url_get;
1.1 millert 616: }
617:
618: /*
619: * Read the rest of the header.
620: */
1.40 fgsch 621: free(buf);
622: filesize = -1;
623:
1.62 ray 624: for (;;) {
1.61 deraadt 625: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 626: warn("Receiving HTTP reply");
627: goto cleanup_url_get;
628: }
1.61 deraadt 629:
1.40 fgsch 630: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
631: buf[--len] = '\0';
632: if (len == 0)
1.1 millert 633: break;
1.80 martynas 634: #ifndef SMALL
1.40 fgsch 635: if (debug)
636: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 637: #endif /* !SMALL */
1.1 millert 638:
1.40 fgsch 639: /* Look for some headers */
640: cp = buf;
1.1 millert 641: #define CONTENTLEN "Content-Length: "
1.40 fgsch 642: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
643: cp += sizeof(CONTENTLEN) - 1;
1.58 grunk 644: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
645: if (errstr != NULL)
1.40 fgsch 646: goto improper;
1.75 martynas 647: #ifndef SMALL
1.90 martynas 648: if (restart_point)
1.75 martynas 649: filesize += restart_point;
1.78 martynas 650: #endif /* !SMALL */
1.40 fgsch 651: #define LOCATION "Location: "
652: } else if (isredirect &&
653: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
654: cp += sizeof(LOCATION) - 1;
655: if (verbose)
656: fprintf(ttyout, "Redirected to %s\n", cp);
657: if (fin != NULL)
658: fclose(fin);
659: else if (s != -1)
660: close(s);
1.67 steven 661: free(proxyurl);
1.62 ray 662: free(newline);
1.81 espie 663: free(cookie);
1.40 fgsch 664: rval = url_get(cp, proxyenv, outfile);
1.67 steven 665: free(buf);
1.40 fgsch 666: return (rval);
667: }
1.1 millert 668: }
669:
1.17 millert 670: /* Open the output file. */
671: if (strcmp(savefile, "-") != 0) {
1.75 martynas 672: #ifndef SMALL
673: if (resume)
1.83 martynas 674: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
675: 0666);
1.75 martynas 676: else
1.78 martynas 677: #endif /* !SMALL */
1.75 martynas 678: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
679: 0666);
1.10 deraadt 680: if (out < 0) {
681: warn("Can't open %s", savefile);
682: goto cleanup_url_get;
683: }
684: } else
1.17 millert 685: out = fileno(stdout);
1.1 millert 686:
687: /* Trap signals */
688: oldintr = NULL;
689: if (setjmp(httpabort)) {
690: if (oldintr)
1.2 millert 691: (void)signal(SIGINT, oldintr);
1.6 millert 692: goto cleanup_url_get;
1.1 millert 693: }
694: oldintr = signal(SIGINT, aborthttp);
695:
696: bytes = 0;
697: hashbytes = mark;
1.84 martynas 698: progressmeter(-1, path);
1.43 millert 699:
700: free(buf);
1.1 millert 701:
702: /* Finally, suck down the file. */
1.40 fgsch 703: if ((buf = malloc(4096)) == NULL)
1.47 deraadt 704: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 705: i = 0;
1.61 deraadt 706: len = 1;
707: while (len > 0) {
708: len = ftp_read(fin, ssl, buf, 4096);
1.1 millert 709: bytes += len;
1.61 deraadt 710: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
711: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 712: warn("Writing %s", savefile);
1.6 millert 713: goto cleanup_url_get;
1.1 millert 714: }
715: else if (i == 0)
716: break;
717: }
718: if (hash && !progress) {
719: while (bytes >= hashbytes) {
1.10 deraadt 720: (void)putc('#', ttyout);
1.1 millert 721: hashbytes += mark;
722: }
1.10 deraadt 723: (void)fflush(ttyout);
1.1 millert 724: }
725: }
726: if (hash && !progress && bytes > 0) {
727: if (bytes < mark)
1.10 deraadt 728: (void)putc('#', ttyout);
729: (void)putc('\n', ttyout);
730: (void)fflush(ttyout);
1.1 millert 731: }
732: if (len != 0) {
733: warn("Reading from socket");
1.6 millert 734: goto cleanup_url_get;
1.1 millert 735: }
1.84 martynas 736: progressmeter(1, NULL);
1.75 martynas 737: if (
738: #ifndef SMALL
739: !resume &&
1.78 martynas 740: #endif /* !SMALL */
1.75 martynas 741: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 742: if (verbose)
743: fputs("Read short file.\n", ttyout);
744: goto cleanup_url_get;
745: }
746:
1.1 millert 747: if (verbose)
1.10 deraadt 748: fputs("Successfully retrieved file.\n", ttyout);
1.2 millert 749: (void)signal(SIGINT, oldintr);
1.1 millert 750:
1.40 fgsch 751: rval = 0;
752: goto cleanup_url_get;
1.1 millert 753:
1.14 millert 754: noftpautologin:
755: warnx(
756: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
757: goto cleanup_url_get;
758:
1.1 millert 759: improper:
1.8 millert 760: warnx("Improper response from %s", host);
1.14 millert 761:
1.6 millert 762: cleanup_url_get:
1.61 deraadt 763: #ifndef SMALL
764: if (ssl) {
765: SSL_shutdown(ssl);
766: SSL_free(ssl);
767: }
1.78 martynas 768: #endif /* !SMALL */
1.40 fgsch 769: if (fin != NULL)
770: fclose(fin);
771: else if (s != -1)
1.1 millert 772: close(s);
1.67 steven 773: free(buf);
774: free(proxyurl);
1.62 ray 775: free(newline);
1.81 espie 776: free(cookie);
1.40 fgsch 777: return (rval);
1.1 millert 778: }
779:
780: /*
781: * Abort a http retrieval
782: */
1.51 deraadt 783: /* ARGSUSED */
1.1 millert 784: void
1.51 deraadt 785: aborthttp(int signo)
1.1 millert 786: {
787:
788: alarmtimer(0);
1.10 deraadt 789: fputs("\nhttp fetch aborted.\n", ttyout);
790: (void)fflush(ttyout);
1.1 millert 791: longjmp(httpabort, 1);
792: }
793:
794: /*
1.22 deraadt 795: * Abort a http retrieval
796: */
1.51 deraadt 797: /* ARGSUSED */
1.22 deraadt 798: void
1.51 deraadt 799: abortfile(int signo)
1.22 deraadt 800: {
801:
802: alarmtimer(0);
803: fputs("\nfile fetch aborted.\n", ttyout);
804: (void)fflush(ttyout);
805: longjmp(httpabort, 1);
806: }
807:
808: /*
1.1 millert 809: * Retrieve multiple files from the command line, transferring
810: * files of the form "host:path", "ftp://host/path" using the
811: * ftp protocol, and files of the form "http://host/path" using
812: * the http protocol.
1.2 millert 813: * If path has a trailing "/", then return (-1);
1.1 millert 814: * the path will be cd-ed into and the connection remains open,
815: * and the function will return -1 (to indicate the connection
816: * is alive).
817: * If an error occurs the return value will be the offset+1 in
818: * argv[] of the file that caused a problem (i.e, argv[x]
819: * returns x+1)
820: * Otherwise, 0 is returned if all files retrieved successfully.
821: */
822: int
1.50 deraadt 823: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 824: {
825: char *xargv[5];
1.62 ray 826: char *cp, *url, *host, *dir, *file, *portnum;
827: char *username, *pass, *pathstart;
1.6 millert 828: char *ftpproxy, *httpproxy;
1.14 millert 829: int rval, xargc;
830: volatile int argpos;
1.49 krw 831: int dirhasglob, filehasglob, oautologin;
1.14 millert 832: char rempath[MAXPATHLEN];
1.1 millert 833:
834: argpos = 0;
835:
836: if (setjmp(toplevel)) {
837: if (connected)
838: disconnect(0, NULL);
1.2 millert 839: return (argpos + 1);
1.1 millert 840: }
1.3 millert 841: (void)signal(SIGINT, (sig_t)intr);
842: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 843:
1.45 millert 844: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
845: ftpproxy = NULL;
846: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
847: httpproxy = NULL;
1.6 millert 848:
1.1 millert 849: /*
850: * Loop through as long as there's files to fetch.
851: */
1.62 ray 852: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 853: if (strchr(argv[argpos], ':') == NULL)
854: break;
1.62 ray 855: host = dir = file = portnum = username = pass = NULL;
1.1 millert 856:
857: /*
858: * We muck with the string, so we make a copy.
859: */
1.62 ray 860: url = strdup(argv[argpos]);
861: if (url == NULL)
1.1 millert 862: errx(1, "Can't allocate memory for auto-fetch.");
863:
864: /*
865: * Try HTTP URL-style arguments first.
866: */
1.62 ray 867: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 868: #ifndef SMALL
869: /* even if we compiled without SSL, url_get will check */
1.62 ray 870: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 871: #endif /* !SMALL */
1.62 ray 872: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 873: redirect_loop = 0;
1.62 ray 874: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 875: rval = argpos + 1;
876: continue;
877: }
878:
879: /*
1.6 millert 880: * Try FTP URL-style arguments next. If ftpproxy is
881: * set, use url_get() instead of standard ftp.
882: * Finally, try host:file.
1.1 millert 883: */
1.62 ray 884: host = url;
885: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 886: char *passend, *passagain, *userend;
1.31 itojun 887:
1.6 millert 888: if (ftpproxy) {
1.62 ray 889: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 890: rval = argpos + 1;
891: continue;
892: }
1.1 millert 893: host += sizeof(FTP_URL) - 1;
1.8 millert 894: dir = strchr(host, '/');
1.1 millert 895:
1.8 millert 896: /* Look for [user:pass@]host[:port] */
1.31 itojun 897:
898: /* check if we have "user:pass@" */
1.37 heko 899: userend = strchr(host, ':');
1.31 itojun 900: passend = strchr(host, '@');
901: if (passend && userend && userend < passend &&
902: (!dir || passend < dir)) {
1.62 ray 903: username = host;
1.31 itojun 904: pass = userend + 1;
905: host = passend + 1;
906: *userend = *passend = '\0';
1.37 heko 907: passagain = strchr(host, '@');
1.42 deraadt 908: if (strchr(pass, '@') != NULL ||
1.37 heko 909: (passagain != NULL && passagain < dir)) {
910: warnx(at_encoding_warning);
911: goto bad_ftp_url;
1.42 deraadt 912: }
1.31 itojun 913:
1.77 martynas 914: if (EMPTYSTRING(username)) {
1.11 millert 915: bad_ftp_url:
1.31 itojun 916: warnx("Invalid URL: %s", argv[argpos]);
917: rval = argpos + 1;
918: continue;
919: }
1.62 ray 920: username = urldecode(username);
1.37 heko 921: pass = urldecode(pass);
1.8 millert 922: }
1.31 itojun 923:
924: #ifdef INET6
925: /* check [host]:port, or [host] */
926: if (host[0] == '[') {
927: cp = strchr(host, ']');
928: if (cp && (!dir || cp < dir)) {
929: if (cp + 1 == dir || cp[1] == ':') {
930: host++;
931: *cp++ = '\0';
932: } else
933: cp = NULL;
934: } else
935: cp = host;
1.25 itojun 936: } else
937: cp = host;
1.31 itojun 938: #else
939: cp = host;
1.25 itojun 940: #endif
1.31 itojun 941:
942: /* split off host[:port] if there is */
943: if (cp) {
944: portnum = strchr(cp, ':');
1.52 henning 945: pathstart = strchr(cp, '/');
946: /* : in path is not a port # indicator */
947: if (portnum && pathstart &&
948: pathstart < portnum)
949: portnum = NULL;
950:
1.31 itojun 951: if (!portnum)
952: ;
953: else {
954: if (!dir)
955: ;
956: else if (portnum + 1 < dir) {
957: *portnum++ = '\0';
958: /*
959: * XXX should check if portnum
960: * is decimal number
961: */
962: } else {
963: /* empty portnum */
964: goto bad_ftp_url;
965: }
966: }
967: } else
968: portnum = NULL;
1.8 millert 969: } else { /* classic style `host:file' */
970: dir = strchr(host, ':');
971: }
1.1 millert 972: if (EMPTYSTRING(host)) {
973: rval = argpos + 1;
974: continue;
975: }
976:
977: /*
1.9 millert 978: * If dir is NULL, the file wasn't specified
1.1 millert 979: * (URL looked something like ftp://host)
980: */
1.8 millert 981: if (dir != NULL)
982: *dir++ = '\0';
1.1 millert 983:
984: /*
985: * Extract the file and (if present) directory name.
986: */
1.42 deraadt 987: if (!EMPTYSTRING(dir)) {
1.8 millert 988: cp = strrchr(dir, '/');
1.1 millert 989: if (cp != NULL) {
990: *cp++ = '\0';
991: file = cp;
992: } else {
993: file = dir;
994: dir = NULL;
995: }
996: }
1.80 martynas 997: #ifndef SMALL
1.1 millert 998: if (debug)
1.42 deraadt 999: fprintf(ttyout,
1000: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1001: username, pass ? "XXXX" : NULL, host, portnum,
1002: dir, file);
1.80 martynas 1003: #endif /* !SMALL */
1.1 millert 1004:
1005: /*
1.49 krw 1006: * Set up the connection.
1.1 millert 1007: */
1.49 krw 1008: if (connected)
1009: disconnect(0, NULL);
1010: xargv[0] = __progname;
1011: xargv[1] = host;
1.8 millert 1012: xargv[2] = NULL;
1.49 krw 1013: xargc = 2;
1014: if (!EMPTYSTRING(portnum)) {
1015: xargv[2] = portnum;
1016: xargv[3] = NULL;
1017: xargc = 3;
1018: }
1019: oautologin = autologin;
1.88 martynas 1020: if (username == NULL)
1.89 halex 1021: anonftp = 1;
1022: else {
1023: anonftp = 0;
1.49 krw 1024: autologin = 0;
1.89 halex 1025: }
1.49 krw 1026: setpeer(xargc, xargv);
1027: autologin = oautologin;
1.87 martynas 1028: if (connected == 0 ||
1029: (connected == 1 && autologin && (username == NULL ||
1030: !ftp_login(host, username, pass)))) {
1.49 krw 1031: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1032: rval = argpos + 1;
1033: continue;
1.1 millert 1034: }
1.49 krw 1035:
1036: /* Always use binary transfers. */
1037: setbinary(0, NULL);
1.1 millert 1038:
1.4 millert 1039: dirhasglob = filehasglob = 0;
1040: if (doglob) {
1.42 deraadt 1041: if (!EMPTYSTRING(dir) &&
1.4 millert 1042: strpbrk(dir, "*?[]{}") != NULL)
1043: dirhasglob = 1;
1.42 deraadt 1044: if (!EMPTYSTRING(file) &&
1.4 millert 1045: strpbrk(file, "*?[]{}") != NULL)
1046: filehasglob = 1;
1047: }
1048:
1.1 millert 1049: /* Change directories, if necessary. */
1.42 deraadt 1050: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1051: xargv[0] = "cd";
1052: xargv[1] = dir;
1053: xargv[2] = NULL;
1054: cd(2, xargv);
1.42 deraadt 1055: if (!dirchange) {
1.1 millert 1056: rval = argpos + 1;
1057: continue;
1058: }
1059: }
1060:
1061: if (EMPTYSTRING(file)) {
1.86 martynas 1062: #ifndef SMALL
1.1 millert 1063: rval = -1;
1.86 martynas 1064: #else /* !SMALL */
1065: recvrequest("NLST", "-", NULL, "w", 0, 0);
1066: rval = 0;
1067: #endif /* !SMALL */
1.1 millert 1068: continue;
1069: }
1070:
1.21 marc 1071: if (verbose)
1.10 deraadt 1072: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1073:
1.4 millert 1074: if (dirhasglob) {
1075: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1076: file = rempath;
1077: }
1078:
1079: /* Fetch the file(s). */
1.10 deraadt 1080: xargc = 2;
1.1 millert 1081: xargv[0] = "get";
1082: xargv[1] = file;
1083: xargv[2] = NULL;
1.4 millert 1084: if (dirhasglob || filehasglob) {
1085: int ointeractive;
1086:
1087: ointeractive = interactive;
1088: interactive = 0;
1089: xargv[0] = "mget";
1.78 martynas 1090: #ifndef SMALL
1091: if (resume) {
1092: xargc = 3;
1093: xargv[1] = "-c";
1094: xargv[2] = file;
1095: xargv[3] = NULL;
1096: }
1097: #endif /* !SMALL */
1.10 deraadt 1098: mget(xargc, xargv);
1.5 millert 1099: interactive = ointeractive;
1.10 deraadt 1100: } else {
1.17 millert 1101: if (outfile != NULL) {
1102: xargv[2] = outfile;
1103: xargv[3] = NULL;
1.10 deraadt 1104: xargc++;
1105: }
1.75 martynas 1106: #ifndef SMALL
1107: if (resume)
1108: reget(xargc, xargv);
1109: else
1.78 martynas 1110: #endif /* !SMALL */
1.75 martynas 1111: get(xargc, xargv);
1.10 deraadt 1112: }
1.1 millert 1113:
1.4 millert 1114: if ((code / 100) != COMPLETE)
1.1 millert 1115: rval = argpos + 1;
1116: }
1117: if (connected && rval != -1)
1118: disconnect(0, NULL);
1119: return (rval);
1.37 heko 1120: }
1121:
1122: char *
1.50 deraadt 1123: urldecode(const char *str)
1.37 heko 1124: {
1.53 deraadt 1125: char *ret, c;
1126: int i, reallen;
1.37 heko 1127:
1.53 deraadt 1128: if (str == NULL)
1129: return NULL;
1130: if ((ret = malloc(strlen(str)+1)) == NULL)
1131: err(1, "Can't allocate memory for URL decoding");
1132: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1133: c = str[i];
1134: if (c == '+') {
1135: *ret = ' ';
1136: continue;
1137: }
1.61 deraadt 1138:
1139: /* Cannot use strtol here because next char
1140: * after %xx may be a digit.
1141: */
1.53 deraadt 1142: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1143: *ret = hextochar(&str[i+1]);
1144: i+=2;
1145: continue;
1146: }
1147: *ret = c;
1148: }
1149: *ret = '\0';
1150:
1151: return ret-reallen;
1.37 heko 1152: }
1153:
1154: char
1.50 deraadt 1155: hextochar(const char *str)
1.37 heko 1156: {
1.53 deraadt 1157: char c, ret;
1.37 heko 1158:
1.53 deraadt 1159: c = str[0];
1160: ret = c;
1161: if (isalpha(c))
1162: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1163: else
1164: ret -= '0';
1165: ret *= 16;
1166:
1167: c = str[1];
1168: ret += c;
1169: if (isalpha(c))
1170: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1171: else
1172: ret -= '0';
1173: return ret;
1.25 itojun 1174: }
1175:
1176: int
1.50 deraadt 1177: isurl(const char *p)
1.25 itojun 1178: {
1.27 millert 1179:
1.26 deraadt 1180: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1181: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1182: #ifndef SMALL
1183: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1184: #endif /* !SMALL */
1.27 millert 1185: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1186: strstr(p, ":/"))
1187: return (1);
1188: return (0);
1.1 millert 1189: }
1.61 deraadt 1190:
1191: char *
1192: ftp_readline(FILE *fp, SSL *ssl, size_t *lenp)
1193: {
1194: if (fp != NULL)
1195: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1196: #ifndef SMALL
1197: else if (ssl != NULL)
1198: return SSL_readline(ssl, lenp);
1.78 martynas 1199: #endif /* !SMALL */
1.61 deraadt 1200: else
1201: return NULL;
1202: }
1203:
1.65 ray 1204: size_t
1.61 deraadt 1205: ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len)
1206: {
1.65 ray 1207: size_t ret;
1.61 deraadt 1208: if (fp != NULL)
1209: ret = fread(buf, sizeof(char), len, fp);
1210: #ifndef SMALL
1.65 ray 1211: else if (ssl != NULL) {
1212: int nr;
1213:
1214: if (len > INT_MAX)
1215: len = INT_MAX;
1216: if ((nr = SSL_read(ssl, buf, (int)len)) <= 0)
1217: ret = 0;
1218: else
1219: ret = nr;
1220: }
1.78 martynas 1221: #endif /* !SMALL */
1.61 deraadt 1222: else
1223: ret = 0;
1224: return (ret);
1225: }
1226:
1227: int
1228: ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...)
1229: {
1230: int ret;
1231: va_list ap;
1232:
1233: va_start(ap, fmt);
1234:
1235: if (fp != NULL)
1236: ret = vfprintf(fp, fmt, ap);
1237: #ifndef SMALL
1238: else if (ssl != NULL)
1239: ret = SSL_vprintf((SSL*)ssl, fmt, ap);
1.78 martynas 1240: #endif /* !SMALL */
1.61 deraadt 1241: else
1.92 jsg 1242: ret = 0;
1.61 deraadt 1243:
1244: va_end(ap);
1245: return (ret);
1246: }
1247:
1248: #ifndef SMALL
1249: int
1250: SSL_vprintf(SSL *ssl, const char *fmt, va_list ap)
1251: {
1252: int ret;
1253: char *string;
1254:
1255: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1256: return ret;
1.64 ray 1257: ret = SSL_write(ssl, string, ret);
1.61 deraadt 1258: free(string);
1259: return ret;
1260: }
1261:
1262: char *
1263: SSL_readline(SSL *ssl, size_t *lenp)
1264: {
1.63 ray 1265: size_t i, len;
1.61 deraadt 1266: char *buf, *q, c;
1267:
1268: len = 128;
1269: if ((buf = malloc(len)) == NULL)
1270: errx(1, "Can't allocate memory for transfer buffer");
1271: for (i = 0; ; i++) {
1272: if (i >= len - 1) {
1273: if ((q = realloc(buf, 2 * len)) == NULL)
1274: errx(1, "Can't expand transfer buffer");
1275: buf = q;
1276: len *= 2;
1277: }
1278: if (SSL_read(ssl, &c, 1) <= 0)
1279: break;
1280: buf[i] = c;
1281: if (c == '\n')
1282: break;
1283: }
1284: *lenp = i;
1285: return (buf);
1286: }
1287:
1288: int
1.81 espie 1289: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1290: {
1.66 ray 1291: int l;
1.61 deraadt 1292: char buf[1024];
1293: char *connstr, *hosttail, *port;
1294:
1295: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1296: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1297: host++;
1298: *hosttail++ = '\0';
1299: } else
1300: hosttail = host;
1301:
1302: port = strrchr(hosttail, ':'); /* find portnum */
1303: if (port != NULL)
1304: *port++ = '\0';
1305: if (!port)
1306: port = "443";
1307:
1.81 espie 1308: if (cookie) {
1309: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1310: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1311: host, port, cookie, HTTP_USER_AGENT);
1312: } else {
1313: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1314: host, port, HTTP_USER_AGENT);
1315: }
1316:
1.66 ray 1317: if (l == -1)
1.61 deraadt 1318: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1319: #ifndef SMALL
1.68 ray 1320: if (debug)
1321: printf("%s", connstr);
1.80 martynas 1322: #endif /* !SMALL */
1.66 ray 1323: if (write(socket, connstr, l) != l)
1.68 ray 1324: err(1, "Could not send connect string");
1.61 deraadt 1325: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1326: free(connstr);
1.61 deraadt 1327: return(200);
1328: }
1.78 martynas 1329: #endif /* !SMALL */