GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ftp/fetch.c Lines: 0 790 0.0 %
Date: 2016-12-06 Branches: 0 659 0.0 %

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