1/*	$NetBSD: ftp.c,v 1.5 2011/08/17 09:19:38 christos Exp $	*/
2/*-
3 * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
4 * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $
31 */
32
33/*
34 * Portions of this code were taken from or based on ftpio.c:
35 *
36 * ----------------------------------------------------------------------------
37 * "THE BEER-WARE LICENSE" (Revision 42):
38 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
39 * can do whatever you want with this stuff. If we meet some day, and you think
40 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
41 * ----------------------------------------------------------------------------
42 *
43 * Major Changelog:
44 *
45 * Dag-Erling Co�dan Sm�rgrav
46 * 9 Jun 1998
47 *
48 * Incorporated into libfetch
49 *
50 * Jordan K. Hubbard
51 * 17 Jan 1996
52 *
53 * Turned inside out. Now returns xfers as new file ids, not as a special
54 * `state' of FTP_t
55 *
56 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
57 *
58 */
59
60#ifdef __linux__
61/* Keep this down to Linux, it can create surprises else where. */
62#define _GNU_SOURCE
63#endif
64
65#if HAVE_CONFIG_H
66#include "config.h"
67#endif
68#ifndef NETBSD
69#include <nbcompat.h>
70#endif
71
72#include <sys/types.h>
73#include <sys/socket.h>
74
75#include <netinet/in.h>
76#include <arpa/inet.h>
77
78#include <ctype.h>
79#include <errno.h>
80#include <fcntl.h>
81#if defined(HAVE_INTTYPES_H) || defined(NETBSD)
82#include <inttypes.h>
83#endif
84#include <stdarg.h>
85#ifndef NETBSD
86#include <nbcompat/netdb.h>
87#include <nbcompat/stdio.h>
88#else
89#include <netdb.h>
90#include <stdio.h>
91#endif
92#include <stdlib.h>
93#include <string.h>
94#include <time.h>
95#include <unistd.h>
96
97#include "fetch.h"
98#include "common.h"
99#include "ftperr.h"
100
101#define FTP_ANONYMOUS_USER	"anonymous"
102
103#define FTP_CONNECTION_ALREADY_OPEN	125
104#define FTP_OPEN_DATA_CONNECTION	150
105#define FTP_OK				200
106#define FTP_FILE_STATUS			213
107#define FTP_SERVICE_READY		220
108#define FTP_TRANSFER_COMPLETE		226
109#define FTP_PASSIVE_MODE		227
110#define FTP_LPASSIVE_MODE		228
111#define FTP_EPASSIVE_MODE		229
112#define FTP_LOGGED_IN			230
113#define FTP_FILE_ACTION_OK		250
114#define FTP_DIRECTORY_CREATED		257 /* multiple meanings */
115#define FTP_FILE_CREATED		257 /* multiple meanings */
116#define FTP_WORKING_DIRECTORY		257 /* multiple meanings */
117#define FTP_NEED_PASSWORD		331
118#define FTP_NEED_ACCOUNT		332
119#define FTP_FILE_OK			350
120#define FTP_SYNTAX_ERROR		500
121#define FTP_PROTOCOL_ERROR		999
122
123#define isftpreply(foo)				\
124	(isdigit((unsigned char)foo[0]) &&	\
125	    isdigit((unsigned char)foo[1]) &&	\
126	    isdigit((unsigned char)foo[2]) &&	\
127	    (foo[3] == ' ' || foo[3] == '\0'))
128#define isftpinfo(foo) \
129	(isdigit((unsigned char)foo[0]) &&	\
130	    isdigit((unsigned char)foo[1]) &&	\
131	    isdigit((unsigned char)foo[2]) &&	\
132	    foo[3] == '-')
133
134/*
135 * Translate IPv4 mapped IPv6 address to IPv4 address
136 */
137static void
138unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len)
139{
140	struct sockaddr_in *sin4;
141	uint32_t addr;
142	int port;
143
144	if (sin6->sin6_family != AF_INET6 ||
145	    !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
146		return;
147	sin4 = (struct sockaddr_in *)(void *)sin6;
148	addr = *(uint32_t *)(void *)&sin6->sin6_addr.s6_addr[12];
149	port = sin6->sin6_port;
150	memset(sin4, 0, sizeof(struct sockaddr_in));
151	sin4->sin_addr.s_addr = addr;
152	sin4->sin_port = port;
153	sin4->sin_family = AF_INET;
154	*len = sizeof(struct sockaddr_in);
155#ifdef HAVE_SA_LEN
156	sin4->sin_len = sizeof(struct sockaddr_in);
157#endif
158}
159
160/*
161 * Get server response
162 */
163static int
164ftp_chkerr(conn_t *conn)
165{
166	if (fetch_getln(conn) == -1) {
167		fetch_syserr();
168		return (-1);
169	}
170	if (isftpinfo(conn->buf)) {
171		while (conn->buflen && !isftpreply(conn->buf)) {
172			if (fetch_getln(conn) == -1) {
173				fetch_syserr();
174				return (-1);
175			}
176		}
177	}
178
179	while (conn->buflen &&
180	    isspace((unsigned char)conn->buf[conn->buflen - 1]))
181		conn->buflen--;
182	conn->buf[conn->buflen] = '\0';
183
184	if (!isftpreply(conn->buf)) {
185		ftp_seterr(FTP_PROTOCOL_ERROR);
186		return (-1);
187	}
188
189	conn->err = (conn->buf[0] - '0') * 100
190	    + (conn->buf[1] - '0') * 10
191	    + (conn->buf[2] - '0');
192
193	return (conn->err);
194}
195
196/*
197 * Send a command and check reply
198 */
199static int
200ftp_cmd(conn_t *conn, const char *fmt, ...)
201{
202	va_list ap;
203	size_t len;
204	char *msg;
205	ssize_t r;
206
207	va_start(ap, fmt);
208	len = vasprintf(&msg, fmt, ap);
209	va_end(ap);
210
211	if (msg == NULL) {
212		errno = ENOMEM;
213		fetch_syserr();
214		return (-1);
215	}
216
217	r = fetch_write(conn, msg, len);
218	free(msg);
219
220	if (r == -1) {
221		fetch_syserr();
222		return (-1);
223	}
224
225	return (ftp_chkerr(conn));
226}
227
228/*
229 * Return a pointer to the filename part of a path
230 */
231static const char *
232ftp_filename(const char *file, size_t *len, int *type, int subdir)
233{
234	const char *s;
235
236	if ((s = strrchr(file, '/')) == NULL || subdir)
237		s = file;
238	else
239		s = s + 1;
240	*len = strlen(s);
241	if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) {
242		*type = s[*len - 1];
243		*len -= 7;
244	} else {
245		*type = '\0';
246	}
247	return (s);
248}
249
250/*
251 * Get current working directory from the reply to a CWD, PWD or CDUP
252 * command.
253 */
254static int
255ftp_pwd(conn_t *conn, char **pwd)
256{
257	char *src, *dst, *end;
258	int q;
259	size_t len;
260
261	if (conn->err != FTP_WORKING_DIRECTORY &&
262	    conn->err != FTP_FILE_ACTION_OK)
263		return (FTP_PROTOCOL_ERROR);
264	end = conn->buf + conn->buflen;
265	src = conn->buf + 4;
266	if (src >= end || *src++ != '"')
267		return (FTP_PROTOCOL_ERROR);
268	len = end - src + 1;
269	*pwd = malloc(len);
270	if (*pwd == NULL)
271		return (FTP_PROTOCOL_ERROR);
272	for (q = 0, dst = *pwd; src < end; ++src) {
273		if (!q && *src == '"')
274			q = 1;
275		else if (q && *src != '"')
276			break;
277		else if (q)
278			*dst++ = '"', q = 0;
279		else
280			*dst++ = *src;
281	}
282	*dst = '\0';
283	if (**pwd != '/') {
284		free(*pwd);
285		*pwd = NULL;
286		return (FTP_PROTOCOL_ERROR);
287	}
288	return (FTP_OK);
289}
290
291/*
292 * Change working directory to the directory that contains the specified
293 * file.
294 */
295static int
296ftp_cwd(conn_t *conn, const char *path, int subdir)
297{
298	const char *beg, *end;
299	char *pwd, *dst;
300	int e;
301	size_t i, len;
302
303	if (*path != '/') {
304		ftp_seterr(501);
305		return (-1);
306	}
307	++path;
308
309	/* Simple case: still in the home directory and no directory change. */
310	if (conn->ftp_home == NULL && strchr(path, '/') == NULL &&
311	    (!subdir || *path == '\0'))
312		return 0;
313
314	if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
315	    (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
316		ftp_seterr(e);
317		return (-1);
318	}
319	if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) {
320		fetch_syserr();
321		free(pwd);
322		return (-1);
323	}
324	if (*path == '/') {
325		while (path[1] == '/')
326			++path;
327		dst = strdup(path);
328	} else if (strcmp(conn->ftp_home, "/") == 0) {
329		dst = strdup(path - 1);
330	} else {
331		asprintf(&dst, "%s/%s", conn->ftp_home, path);
332	}
333	if (dst == NULL) {
334		fetch_syserr();
335		free(pwd);
336		return (-1);
337	}
338
339	if (subdir)
340		end = dst + strlen(dst);
341	else
342		end = strrchr(dst, '/');
343
344	for (;;) {
345		len = strlen(pwd);
346
347		/* Look for a common prefix between PWD and dir to fetch. */
348		for (i = 0; i <= len && i <= (size_t)(end - dst); ++i)
349			if (pwd[i] != dst[i])
350				break;
351		/* Keep going up a dir until we have a matching prefix. */
352		if (strcmp(pwd, "/") == 0)
353			break;
354		if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/'))
355			break;
356		free(pwd);
357		if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK ||
358		    (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
359		    (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
360			ftp_seterr(e);
361			free(dst);
362			return (-1);
363		}
364	}
365	free(pwd);
366
367#ifdef FTP_COMBINE_CWDS
368	/* Skip leading slashes, even "////". */
369	for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i)
370		/* nothing */ ;
371
372	/* If there is no trailing dir, we're already there. */
373	if (beg >= end) {
374		free(dst);
375		return (0);
376	}
377
378	/* Change to the directory all in one chunk (e.g., foo/bar/baz). */
379	e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg);
380	if (e == FTP_FILE_ACTION_OK) {
381		free(dst);
382		return (0);
383	}
384#endif /* FTP_COMBINE_CWDS */
385
386	/* That didn't work so go back to legacy behavior (multiple CWDs). */
387	for (beg = dst + i; beg < end; beg = dst + i + 1) {
388		while (*beg == '/')
389			++beg, ++i;
390		for (++i; dst + i < end && dst[i] != '/'; ++i)
391			/* nothing */ ;
392		e = ftp_cmd(conn, "CWD %.*s\r\n", dst + i - beg, beg);
393		if (e != FTP_FILE_ACTION_OK) {
394			free(dst);
395			ftp_seterr(e);
396			return (-1);
397		}
398	}
399	free(dst);
400	return (0);
401}
402
403/*
404 * Set transfer mode and data type
405 */
406static int
407ftp_mode_type(conn_t *conn, int mode, int type)
408{
409	int e;
410
411	switch (mode) {
412	case 0:
413	case 's':
414		mode = 'S';
415		/*FALLTHROUGH*/
416	case 'S':
417		break;
418	default:
419		return (FTP_PROTOCOL_ERROR);
420	}
421	if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) {
422		if (mode == 'S') {
423			/*
424			 * Stream mode is supposed to be the default - so
425			 * much so that some servers not only do not
426			 * support any other mode, but do not support the
427			 * MODE command at all.
428			 *
429			 * If "MODE S" fails, it is unlikely that we
430			 * previously succeeded in setting a different
431			 * mode.  Therefore, we simply hope that the
432			 * server is already in the correct mode, and
433			 * silently ignore the failure.
434			 */
435		} else {
436			return (e);
437		}
438	}
439
440	switch (type) {
441	case 0:
442	case 'i':
443		type = 'I';
444		/*FALLTHROUGH*/
445	case 'I':
446		break;
447	case 'a':
448		type = 'A';
449		/*FALLTHROUGH*/
450	case 'A':
451		break;
452	case 'd':
453		type = 'D';
454		/*FALLTHROUGH*/
455	case 'D':
456		/* can't handle yet */
457	default:
458		return (FTP_PROTOCOL_ERROR);
459	}
460	if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK)
461		return (e);
462
463	return (FTP_OK);
464}
465
466/*
467 * Request and parse file stats
468 */
469static int
470ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
471{
472	char *ln;
473	const char *filename;
474	size_t filenamelen;
475	int type;
476	struct tm tm;
477	time_t t;
478	int e;
479
480	us->size = -1;
481	us->atime = us->mtime = 0;
482
483	filename = ftp_filename(file, &filenamelen, &type, 0);
484
485	if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) {
486		ftp_seterr(e);
487		return (-1);
488	}
489
490	e = ftp_cmd(conn, "SIZE %.*s\r\n", filenamelen, filename);
491	if (e != FTP_FILE_STATUS) {
492		ftp_seterr(e);
493		return (-1);
494	}
495	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
496		/* nothing */ ;
497	for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
498		us->size = us->size * 10 + *ln - '0';
499	if (*ln && !isspace((unsigned char)*ln)) {
500		ftp_seterr(FTP_PROTOCOL_ERROR);
501		us->size = -1;
502		return (-1);
503	}
504	if (us->size == 0)
505		us->size = -1;
506
507	e = ftp_cmd(conn, "MDTM %.*s\r\n", filenamelen, filename);
508	if (e != FTP_FILE_STATUS) {
509		ftp_seterr(e);
510		return (-1);
511	}
512	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
513		/* nothing */ ;
514	switch (strspn(ln, "0123456789")) {
515	case 14:
516		break;
517	case 15:
518		ln++;
519		ln[0] = '2';
520		ln[1] = '0';
521		break;
522	default:
523		ftp_seterr(FTP_PROTOCOL_ERROR);
524		return (-1);
525	}
526	if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
527	    &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
528	    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
529		ftp_seterr(FTP_PROTOCOL_ERROR);
530		return (-1);
531	}
532	tm.tm_mon--;
533	tm.tm_year -= 1900;
534	tm.tm_isdst = -1;
535	t = timegm(&tm);
536	if (t == (time_t)-1)
537		t = time(NULL);
538	us->mtime = t;
539	us->atime = t;
540
541	return (0);
542}
543
544/*
545 * I/O functions for FTP
546 */
547struct ftpio {
548	conn_t	*cconn;		/* Control connection */
549	conn_t	*dconn;		/* Data connection */
550	int	 dir;		/* Direction */
551	int	 eof;		/* EOF reached */
552	int	 err;		/* Error code */
553};
554
555static ssize_t	 ftp_readfn(void *, void *, size_t);
556static ssize_t	 ftp_writefn(void *, const void *, size_t);
557static void	 ftp_closefn(void *);
558
559static ssize_t
560ftp_readfn(void *v, void *buf, size_t len)
561{
562	struct ftpio *io;
563	ssize_t r;
564
565	io = (struct ftpio *)v;
566	if (io == NULL) {
567		errno = EBADF;
568		return (-1);
569	}
570	if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
571		errno = EBADF;
572		return (-1);
573	}
574	if (io->err) {
575		errno = io->err;
576		return (-1);
577	}
578	if (io->eof)
579		return (0);
580	r = fetch_read(io->dconn, buf, len);
581	if (r > 0)
582		return (r);
583	if (r == 0) {
584		io->eof = 1;
585		return (0);
586	}
587	if (errno != EINTR)
588		io->err = errno;
589	return (-1);
590}
591
592static ssize_t
593ftp_writefn(void *v, const void *buf, size_t len)
594{
595	struct ftpio *io;
596	ssize_t w;
597
598	io = (struct ftpio *)v;
599	if (io == NULL) {
600		errno = EBADF;
601		return (-1);
602	}
603	if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
604		errno = EBADF;
605		return (-1);
606	}
607	if (io->err) {
608		errno = io->err;
609		return (-1);
610	}
611	w = fetch_write(io->dconn, buf, len);
612	if (w >= 0)
613		return (w);
614	if (errno != EINTR)
615		io->err = errno;
616	return (-1);
617}
618
619static int
620ftp_disconnect(conn_t *conn)
621{
622	ftp_cmd(conn, "QUIT\r\n");
623	return fetch_close(conn);
624}
625
626static void
627ftp_closefn(void *v)
628{
629	struct ftpio *io;
630
631	io = (struct ftpio *)v;
632	if (io == NULL) {
633		errno = EBADF;
634		return;
635	}
636	if (io->dir == -1)
637		return;
638	if (io->cconn == NULL || io->dconn == NULL) {
639		errno = EBADF;
640		return;
641	}
642	fetch_close(io->dconn);
643	io->dconn = NULL;
644	io->dir = -1;
645	(void)ftp_chkerr(io->cconn);
646	fetch_cache_put(io->cconn, ftp_disconnect);
647	free(io);
648	return;
649}
650
651static fetchIO *
652ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
653{
654	struct ftpio *io;
655	fetchIO *f;
656
657	if (cconn == NULL || dconn == NULL)
658		return (NULL);
659	if ((io = malloc(sizeof(*io))) == NULL)
660		return (NULL);
661	io->cconn = cconn;
662	io->dconn = dconn;
663	io->dir = mode;
664	io->eof = io->err = 0;
665	f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn);
666	if (f == NULL)
667		free(io);
668	return (f);
669}
670
671/*
672 * Transfer file
673 */
674static fetchIO *
675ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg,
676    int mode, off_t offset, const char *flags)
677{
678	union anonymous {
679		struct sockaddr_storage ss;
680		struct sockaddr sa;
681		struct sockaddr_in6 sin6;
682		struct sockaddr_in sin4;
683	} u;
684	const char *bindaddr;
685	const char *filename;
686	size_t filenamelen;
687	int type;
688	int low, pasv, verbose;
689	int e, sd = -1;
690	socklen_t l;
691	char *s;
692	fetchIO *df;
693
694	/* check flags */
695	low = CHECK_FLAG('l');
696	pasv = !CHECK_FLAG('a');
697	verbose = CHECK_FLAG('v');
698
699	/* passive mode */
700	if (!pasv)
701		pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
702		    strncasecmp(s, "no", 2) != 0);
703
704	/* isolate filename */
705	filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL);
706
707	/* set transfer mode and data type */
708	if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK)
709		goto ouch;
710
711	/* find our own address, bind, and listen */
712	l = sizeof(u.ss);
713	if (getsockname(conn->sd, &u.sa, &l) == -1)
714		goto sysouch;
715	if (u.ss.ss_family == AF_INET6)
716		unmappedaddr(&u.sin6, &l);
717
718retry_mode:
719
720	/* open data socket */
721	if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
722		fetch_syserr();
723		return (NULL);
724	}
725
726	if (pasv) {
727		unsigned char addr[64];
728		char *ln, *p;
729		unsigned int i;
730		int port;
731
732		/* send PASV command */
733		if (verbose)
734			fetch_info("setting passive mode");
735		switch (u.ss.ss_family) {
736		case AF_INET:
737			if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE)
738				goto ouch;
739			break;
740		case AF_INET6:
741			if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) {
742				if (e == -1)
743					goto ouch;
744				if ((e = ftp_cmd(conn, "LPSV\r\n")) !=
745				    FTP_LPASSIVE_MODE)
746					goto ouch;
747			}
748			break;
749		default:
750			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
751			goto ouch;
752		}
753
754		/*
755		 * Find address and port number. The reply to the PASV command
756		 * is IMHO the one and only weak point in the FTP protocol.
757		 */
758		ln = conn->buf;
759		switch (e) {
760		case FTP_PASSIVE_MODE:
761		case FTP_LPASSIVE_MODE:
762			for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
763				/* nothing */ ;
764			if (!*p) {
765				e = FTP_PROTOCOL_ERROR;
766				goto ouch;
767			}
768			l = (e == FTP_PASSIVE_MODE ? 6 : 21);
769			for (i = 0; *p && i < l; i++, p++)
770				addr[i] = (unsigned char)strtol(p, &p, 10);
771			if (i < l) {
772				e = FTP_PROTOCOL_ERROR;
773				goto ouch;
774			}
775			break;
776		case FTP_EPASSIVE_MODE:
777			for (p = ln + 3; *p && *p != '('; p++)
778				/* nothing */ ;
779			if (!*p) {
780				e = FTP_PROTOCOL_ERROR;
781				goto ouch;
782			}
783			++p;
784			if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
785				&port, &addr[3]) != 5 ||
786			    addr[0] != addr[1] ||
787			    addr[0] != addr[2] || addr[0] != addr[3]) {
788				e = FTP_PROTOCOL_ERROR;
789				goto ouch;
790			}
791			break;
792		case FTP_SYNTAX_ERROR:
793			if (verbose)
794				fetch_info("passive mode failed");
795			/* Close socket and retry with passive mode. */
796			pasv = 0;
797			close(sd);
798			sd = -1;
799			goto retry_mode;
800		}
801
802		/* seek to required offset */
803		if (offset)
804			if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK)
805				goto sysouch;
806
807		/* construct sockaddr for data socket */
808		l = sizeof(u.ss);
809		if (getpeername(conn->sd, &u.sa, &l) == -1)
810			goto sysouch;
811		if (u.ss.ss_family == AF_INET6)
812			unmappedaddr(&u.sin6, &l);
813		switch (u.ss.ss_family) {
814		case AF_INET6:
815			if (e == FTP_EPASSIVE_MODE)
816				u.sin6.sin6_port = htons(port);
817			else {
818				memcpy(&u.sin6.sin6_addr, addr + 2, 16);
819				memcpy(&u.sin6.sin6_port, addr + 19, 2);
820			}
821			break;
822		case AF_INET:
823			if (e == FTP_EPASSIVE_MODE)
824				u.sin4.sin_port = htons(port);
825			else {
826				memcpy(&u.sin4.sin_addr, addr, 4);
827				memcpy(&u.sin4.sin_port, addr + 4, 2);
828			}
829			break;
830		default:
831			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
832			break;
833		}
834
835		/* connect to data port */
836		if (verbose)
837			fetch_info("opening data connection");
838		bindaddr = getenv("FETCH_BIND_ADDRESS");
839		if (bindaddr != NULL && *bindaddr != '\0' &&
840		    fetch_bind(sd, u.ss.ss_family, bindaddr) != 0)
841			goto sysouch;
842		if (connect(sd, &u.sa, l) == -1)
843			goto sysouch;
844
845		/* make the server initiate the transfer */
846		if (verbose)
847			fetch_info("initiating transfer");
848		if (op_arg)
849			e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
850		else
851			e = ftp_cmd(conn, "%s %.*s\r\n", oper,
852			    filenamelen, filename);
853		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
854			goto ouch;
855
856	} else {
857		uint32_t a;
858		uint16_t p;
859#if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE)
860		int arg;
861#endif
862		int d;
863		char hname[INET6_ADDRSTRLEN];
864
865		switch (u.ss.ss_family) {
866		case AF_INET6:
867			u.sin6.sin6_port = 0;
868#ifdef IPV6_PORTRANGE
869			arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
870			if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
871				&arg, (socklen_t)sizeof(arg)) == -1)
872				goto sysouch;
873#endif
874			break;
875		case AF_INET:
876			u.sin4.sin_port = 0;
877#ifdef IP_PORTRANGE
878			arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
879			if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
880				&arg, (socklen_t)sizeof(arg)) == -1)
881				goto sysouch;
882#endif
883			break;
884		}
885		if (verbose)
886			fetch_info("binding data socket");
887		if (bind(sd, &u.sa, l) == -1)
888			goto sysouch;
889		if (listen(sd, 1) == -1)
890			goto sysouch;
891
892		/* find what port we're on and tell the server */
893		if (getsockname(sd, &u.sa, &l) == -1)
894			goto sysouch;
895		switch (u.ss.ss_family) {
896		case AF_INET:
897			a = ntohl(u.sin4.sin_addr.s_addr);
898			p = ntohs(u.sin4.sin_port);
899			e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n",
900			    (a >> 24) & 0xff, (a >> 16) & 0xff,
901			    (a >> 8) & 0xff, a & 0xff,
902			    ((unsigned int)p >> 8) & 0xff, p & 0xff);
903			break;
904		case AF_INET6:
905			e = -1;
906			u.sin6.sin6_scope_id = 0;
907			if (getnameinfo(&u.sa, l,
908				hname, (socklen_t)sizeof(hname),
909				NULL, 0, NI_NUMERICHOST) == 0) {
910				e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname,
911				    htons(u.sin6.sin6_port));
912				if (e == -1)
913					goto ouch;
914			}
915			if (e != FTP_OK) {
916				uint8_t aa[sizeof(u.sin6.sin6_addr)];
917				memcpy(aa, &u.sin6.sin6_addr, sizeof(aa));
918				p = ntohs(u.sin6.sin6_port);
919				e = ftp_cmd(conn,
920				    "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",
921				    6, 16,
922				    aa[ 0], aa[ 1], aa[ 2], aa[ 3],
923				    aa[ 4], aa[ 5], aa[ 6], aa[ 7],
924				    aa[ 8], aa[ 9], aa[10], aa[11],
925				    aa[12], aa[13], aa[14], aa[15],
926				    2,
927				    ((unsigned int)p >> 8) & 0xff, p & 0xff);
928			}
929			break;
930		default:
931			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
932			goto ouch;
933		}
934		if (e != FTP_OK)
935			goto ouch;
936
937		/* seek to required offset */
938		if (offset)
939			if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK)
940				goto sysouch;
941
942		/* make the server initiate the transfer */
943		if (verbose)
944			fetch_info("initiating transfer");
945		if (op_arg)
946			e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
947		else
948			e = ftp_cmd(conn, "%s %.*s\r\n", oper,
949			    filenamelen, filename);
950		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
951			goto ouch;
952
953		/* accept the incoming connection and go to town */
954		if ((d = accept(sd, NULL, NULL)) == -1)
955			goto sysouch;
956		close(sd);
957		sd = d;
958	}
959
960	if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
961		goto sysouch;
962	return (df);
963
964sysouch:
965	fetch_syserr();
966	if (sd >= 0)
967		close(sd);
968	return (NULL);
969
970ouch:
971	if (e != -1)
972		ftp_seterr(e);
973	if (sd >= 0)
974		close(sd);
975	return (NULL);
976}
977
978/*
979 * Authenticate
980 */
981static int
982ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
983{
984	const char *user, *pwd, *login_name;
985	char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1];
986	int e, len;
987
988	/* XXX FTP_AUTH, and maybe .netrc */
989
990	/* send user name and password */
991	if (url->user[0] == '\0')
992		fetch_netrc_auth(url);
993	user = url->user;
994	if (*user == '\0')
995		user = getenv("FTP_LOGIN");
996	if (user == NULL || *user == '\0')
997		user = FTP_ANONYMOUS_USER;
998	if (purl && url->port == fetch_default_port(url->scheme))
999		e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host);
1000	else if (purl)
1001		e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port);
1002	else
1003		e = ftp_cmd(conn, "USER %s\r\n", user);
1004
1005	/* did the server request a password? */
1006	if (e == FTP_NEED_PASSWORD) {
1007		pwd = url->pwd;
1008		if (*pwd == '\0')
1009			pwd = getenv("FTP_PASSWORD");
1010		if (pwd == NULL || *pwd == '\0') {
1011			if ((login_name = getlogin()) == 0)
1012				login_name = FTP_ANONYMOUS_USER;
1013			if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0)
1014				len = 0;
1015			else if (len > URL_USERLEN + 1)
1016				len = URL_USERLEN + 1;
1017			gethostname(pbuf + len, sizeof(pbuf) - len);
1018			/* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */
1019			pbuf[sizeof(pbuf) - 1] = '\0';
1020			pwd = pbuf;
1021		}
1022		e = ftp_cmd(conn, "PASS %s\r\n", pwd);
1023	}
1024
1025	return (e);
1026}
1027
1028/*
1029 * Log on to FTP server
1030 */
1031static conn_t *
1032ftp_connect(struct url *url, struct url *purl, const char *flags)
1033{
1034	conn_t *conn;
1035	int e, direct, verbose;
1036#ifdef INET6
1037	int af = AF_UNSPEC;
1038#else
1039	int af = AF_INET;
1040#endif
1041
1042	direct = CHECK_FLAG('d');
1043	verbose = CHECK_FLAG('v');
1044	if (CHECK_FLAG('4'))
1045		af = AF_INET;
1046	else if (CHECK_FLAG('6'))
1047		af = AF_INET6;
1048
1049	if (direct)
1050		purl = NULL;
1051
1052	/* check for proxy */
1053	if (purl) {
1054		/* XXX proxy authentication! */
1055		/* XXX connetion caching */
1056		if (!purl->port)
1057			purl->port = fetch_default_port(purl->scheme);
1058
1059		conn = fetch_connect(purl, af, verbose);
1060	} else {
1061		/* no proxy, go straight to target */
1062		if (!url->port)
1063			url->port = fetch_default_port(url->scheme);
1064
1065		while ((conn = fetch_cache_get(url, af)) != NULL) {
1066			e = ftp_cmd(conn, "NOOP\r\n");
1067			if (e == FTP_OK)
1068				return conn;
1069			fetch_close(conn);
1070		}
1071		conn = fetch_connect(url, af, verbose);
1072		purl = NULL;
1073	}
1074
1075	/* check connection */
1076	if (conn == NULL)
1077		/* fetch_connect() has already set an error code */
1078		return (NULL);
1079
1080	/* expect welcome message */
1081	if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
1082		goto fouch;
1083
1084	/* authenticate */
1085	if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
1086		goto fouch;
1087
1088	/* TODO: Request extended features supported, if any (RFC 3659). */
1089
1090	/* done */
1091	return (conn);
1092
1093fouch:
1094	if (e != -1)
1095		ftp_seterr(e);
1096	fetch_close(conn);
1097	return (NULL);
1098}
1099
1100/*
1101 * Check the proxy settings
1102 */
1103static struct url *
1104ftp_get_proxy(struct url * url, const char *flags)
1105{
1106	struct url *purl;
1107	char *p, *fp, *FP, *hp, *HP;
1108
1109	if (flags != NULL && strchr(flags, 'd') != NULL)
1110		return NULL;
1111	if (fetch_no_proxy_match(url->host))
1112		return NULL;
1113
1114	FP = getenv("FTP_PROXY");
1115	fp = getenv("ftp_proxy");
1116	HP = getenv("HTTP_PROXY");
1117	hp = getenv("http_proxy");
1118
1119	if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) &&
1120	    *p && (purl = fetchParseURL(p)) != NULL) {
1121		if (!*purl->scheme) {
1122			if (fp || FP)
1123				strcpy(purl->scheme, SCHEME_FTP);
1124			else
1125				strcpy(purl->scheme, SCHEME_HTTP);
1126		}
1127		if (!purl->port)
1128			purl->port = fetch_default_proxy_port(purl->scheme);
1129		if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
1130		    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
1131			return purl;
1132		fetchFreeURL(purl);
1133	}
1134	return NULL;
1135}
1136
1137/*
1138 * Process an FTP request
1139 */
1140fetchIO *
1141ftp_request(struct url *url, const char *op, const char *op_arg,
1142    struct url_stat *us, struct url *purl, const char *flags)
1143{
1144	fetchIO *f;
1145	char *path;
1146	conn_t *conn;
1147	int if_modified_since, oflag;
1148	struct url_stat local_us;
1149
1150	/* check if we should use HTTP instead */
1151	if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
1152		if (strcmp(op, "STAT") == 0)
1153			return (http_request(url, "HEAD", us, purl, flags));
1154		else if (strcmp(op, "RETR") == 0)
1155			return (http_request(url, "GET", us, purl, flags));
1156		/*
1157		 * Our HTTP code doesn't support PUT requests yet, so try
1158		 * a direct connection.
1159		 */
1160	}
1161
1162	/* connect to server */
1163	conn = ftp_connect(url, purl, flags);
1164	if (purl)
1165		fetchFreeURL(purl);
1166	if (conn == NULL)
1167		return (NULL);
1168
1169	if ((path = fetchUnquotePath(url)) == NULL) {
1170		fetch_syserr();
1171		return NULL;
1172	}
1173
1174	/* change directory */
1175	if (ftp_cwd(conn, path, op_arg != NULL) == -1) {
1176		free(path);
1177		return (NULL);
1178	}
1179
1180	if_modified_since = CHECK_FLAG('i');
1181	if (if_modified_since && us == NULL)
1182		us = &local_us;
1183
1184	/* stat file */
1185	if (us && ftp_stat(conn, path, us) == -1
1186	    && fetchLastErrCode != FETCH_PROTO
1187	    && fetchLastErrCode != FETCH_UNAVAIL) {
1188		free(path);
1189		return (NULL);
1190	}
1191
1192	if (if_modified_since && url->last_modified > 0 &&
1193	    url->last_modified >= us->mtime) {
1194		free(path);
1195		fetchLastErrCode = FETCH_UNCHANGED;
1196		snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged");
1197		return NULL;
1198	}
1199
1200	/* just a stat */
1201	if (strcmp(op, "STAT") == 0) {
1202		free(path);
1203		return fetchIO_unopen(NULL, NULL, NULL, NULL);
1204	}
1205	if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)
1206		oflag = O_WRONLY;
1207	else
1208		oflag = O_RDONLY;
1209
1210	/* initiate the transfer */
1211	f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags));
1212	free(path);
1213	return f;
1214}
1215
1216/*
1217 * Get and stat file
1218 */
1219fetchIO *
1220fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
1221{
1222	return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags));
1223}
1224
1225/*
1226 * Get file
1227 */
1228fetchIO *
1229fetchGetFTP(struct url *url, const char *flags)
1230{
1231	return (fetchXGetFTP(url, NULL, flags));
1232}
1233
1234/*
1235 * Put file
1236 */
1237fetchIO *
1238fetchPutFTP(struct url *url, const char *flags)
1239{
1240	return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL,
1241	    ftp_get_proxy(url, flags), flags));
1242}
1243
1244/*
1245 * Get file stats
1246 */
1247int
1248fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
1249{
1250	fetchIO *f;
1251
1252	f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags);
1253	if (f == NULL)
1254		return (-1);
1255	fetchIO_close(f);
1256	return (0);
1257}
1258
1259/*
1260 * List a directory
1261 */
1262int
1263fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags)
1264{
1265	fetchIO *f;
1266	char buf[2 * PATH_MAX], *eol, *eos;
1267	ssize_t len;
1268	size_t cur_off;
1269	int ret;
1270
1271	/* XXX What about proxies? */
1272	if (pattern == NULL || strcmp(pattern, "*") == 0)
1273		pattern = "";
1274	f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags);
1275	if (f == NULL)
1276		return -1;
1277
1278	cur_off = 0;
1279	ret = 0;
1280
1281	while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) {
1282		cur_off += len;
1283		while ((eol = memchr(buf, '\n', cur_off)) != NULL) {
1284			if (len == eol - buf)
1285				break;
1286			if (eol != buf) {
1287				if (eol[-1] == '\r')
1288					eos = eol - 1;
1289				else
1290					eos = eol;
1291				*eos = '\0';
1292				ret = fetch_add_entry(ue, url, buf, 0);
1293				if (ret)
1294					break;
1295				cur_off -= eol - buf + 1;
1296				memmove(buf, eol + 1, cur_off);
1297			}
1298		}
1299		if (ret)
1300			break;
1301	}
1302	if (cur_off != 0 || len < 0) {
1303		/* Not RFC conform, bail out. */
1304		fetchIO_close(f);
1305		return -1;
1306	}
1307	fetchIO_close(f);
1308	return ret;
1309}
1310