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