File:  [DragonFly] / src / lib / libfetch / ftp.c
Revision 1.3: download - view: text, annotated - select for diffs
Mon Dec 1 22:58:44 2003 UTC (10 years, 8 months ago) by drhodus
Branches: MAIN
CVS tags: HEAD, DragonFly_Stable, DragonFly_Snap29Sep2004, DragonFly_Snap13Sep2004, DragonFly_RELEASE_1_8_Slip, DragonFly_RELEASE_1_8, DragonFly_RELEASE_1_6_Slip, DragonFly_RELEASE_1_6, DragonFly_RELEASE_1_4_Slip, DragonFly_RELEASE_1_4, DragonFly_RELEASE_1_2_Slip, DragonFly_RELEASE_1_2, DragonFly_RELEASE_1_10_Slip, DragonFly_RELEASE_1_10, DragonFly_1_0_REL, DragonFly_1_0_RC1, DragonFly_1_0A_REL

*	don't fclose() a bogus FILE *.

Obtained from: FreeBSD

    1: /*-
    2:  * Copyright (c) 1998 Dag-Erling Co´dan Sm°rgrav
    3:  * All rights reserved.
    4:  *
    5:  * Redistribution and use in source and binary forms, with or without
    6:  * modification, are permitted provided that the following conditions
    7:  * are met:
    8:  * 1. Redistributions of source code must retain the above copyright
    9:  *    notice, this list of conditions and the following disclaimer
   10:  *    in this position and unchanged.
   11:  * 2. Redistributions in binary form must reproduce the above copyright
   12:  *    notice, this list of conditions and the following disclaimer in the
   13:  *    documentation and/or other materials provided with the distribution.
   14:  * 3. The name of the author may not be used to endorse or promote products
   15:  *    derived from this software without specific prior written permission
   16:  *
   17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27:  *
   28:  * $FreeBSD: src/lib/libfetch/ftp.c,v 1.16.2.31 2003/06/06 06:45:25 des Exp $
   29:  * $DragonFly: src/lib/libfetch/ftp.c,v 1.3 2003/12/01 22:58:44 drhodus Exp $
   30:  */
   31: 
   32: /*
   33:  * Portions of this code were taken from or based on ftpio.c:
   34:  *
   35:  * ----------------------------------------------------------------------------
   36:  * "THE BEER-WARE LICENSE" (Revision 42):
   37:  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
   38:  * can do whatever you want with this stuff. If we meet some day, and you think
   39:  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
   40:  * ----------------------------------------------------------------------------
   41:  *
   42:  * Major Changelog:
   43:  *
   44:  * Dag-Erling Co´dan Sm°rgrav
   45:  * 9 Jun 1998
   46:  *
   47:  * Incorporated into libfetch
   48:  *
   49:  * Jordan K. Hubbard
   50:  * 17 Jan 1996
   51:  *
   52:  * Turned inside out. Now returns xfers as new file ids, not as a special
   53:  * `state' of FTP_t
   54:  *
   55:  * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
   56:  *
   57:  */
   58: 
   59: #include <sys/param.h>
   60: #include <sys/socket.h>
   61: #include <netinet/in.h>
   62: 
   63: #include <ctype.h>
   64: #include <err.h>
   65: #include <errno.h>
   66: #include <fcntl.h>
   67: #include <netdb.h>
   68: #include <stdarg.h>
   69: #include <stdio.h>
   70: #include <stdlib.h>
   71: #include <string.h>
   72: #include <time.h>
   73: #include <unistd.h>
   74: 
   75: #include "fetch.h"
   76: #include "common.h"
   77: #include "ftperr.h"
   78: 
   79: #define FTP_ANONYMOUS_USER	"anonymous"
   80: 
   81: #define FTP_CONNECTION_ALREADY_OPEN	125
   82: #define FTP_OPEN_DATA_CONNECTION	150
   83: #define FTP_OK				200
   84: #define FTP_FILE_STATUS			213
   85: #define FTP_SERVICE_READY		220
   86: #define FTP_TRANSFER_COMPLETE		226
   87: #define FTP_PASSIVE_MODE		227
   88: #define FTP_LPASSIVE_MODE		228
   89: #define FTP_EPASSIVE_MODE		229
   90: #define FTP_LOGGED_IN			230
   91: #define FTP_FILE_ACTION_OK		250
   92: #define FTP_NEED_PASSWORD		331
   93: #define FTP_NEED_ACCOUNT		332
   94: #define FTP_FILE_OK			350
   95: #define FTP_SYNTAX_ERROR		500
   96: #define FTP_PROTOCOL_ERROR		999
   97: 
   98: static struct url cached_host;
   99: static conn_t	*cached_connection;
  100: 
  101: #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
  102: 			 && isdigit(foo[2]) \
  103: 			 && (foo[3] == ' ' || foo[3] == '\0'))
  104: #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
  105: 			&& isdigit(foo[2]) && foo[3] == '-')
  106: 
  107: /*
  108:  * Translate IPv4 mapped IPv6 address to IPv4 address
  109:  */
  110: static void
  111: unmappedaddr(struct sockaddr_in6 *sin6)
  112: {
  113: 	struct sockaddr_in *sin4;
  114: 	u_int32_t addr;
  115: 	int port;
  116: 
  117: 	if (sin6->sin6_family != AF_INET6 ||
  118: 	    !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
  119: 		return;
  120: 	sin4 = (struct sockaddr_in *)sin6;
  121: 	addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
  122: 	port = sin6->sin6_port;
  123: 	memset(sin4, 0, sizeof(struct sockaddr_in));
  124: 	sin4->sin_addr.s_addr = addr;
  125: 	sin4->sin_port = port;
  126: 	sin4->sin_family = AF_INET;
  127: 	sin4->sin_len = sizeof(struct sockaddr_in);
  128: }
  129: 
  130: /*
  131:  * Get server response
  132:  */
  133: static int
  134: _ftp_chkerr(conn_t *conn)
  135: {
  136: 	if (_fetch_getln(conn) == -1) {
  137: 		_fetch_syserr();
  138: 		return (-1);
  139: 	}
  140: 	if (isftpinfo(conn->buf)) {
  141: 		while (conn->buflen && !isftpreply(conn->buf)) {
  142: 			if (_fetch_getln(conn) == -1) {
  143: 				_fetch_syserr();
  144: 				return (-1);
  145: 			}
  146: 		}
  147: 	}
  148: 
  149: 	while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
  150: 		conn->buflen--;
  151: 	conn->buf[conn->buflen] = '\0';
  152: 
  153: 	if (!isftpreply(conn->buf)) {
  154: 		_ftp_seterr(FTP_PROTOCOL_ERROR);
  155: 		return (-1);
  156: 	}
  157: 
  158: 	conn->err = (conn->buf[0] - '0') * 100
  159: 	    + (conn->buf[1] - '0') * 10
  160: 	    + (conn->buf[2] - '0');
  161: 
  162: 	return (conn->err);
  163: }
  164: 
  165: /*
  166:  * Send a command and check reply
  167:  */
  168: static int
  169: _ftp_cmd(conn_t *conn, const char *fmt, ...)
  170: {
  171: 	va_list ap;
  172: 	size_t len;
  173: 	char *msg;
  174: 	int r;
  175: 
  176: 	va_start(ap, fmt);
  177: 	len = vasprintf(&msg, fmt, ap);
  178: 	va_end(ap);
  179: 
  180: 	if (msg == NULL) {
  181: 		errno = ENOMEM;
  182: 		_fetch_syserr();
  183: 		return (-1);
  184: 	}
  185: 
  186: 	r = _fetch_putln(conn, msg, len);
  187: 	free(msg);
  188: 
  189: 	if (r == -1) {
  190: 		_fetch_syserr();
  191: 		return (-1);
  192: 	}
  193: 
  194: 	return (_ftp_chkerr(conn));
  195: }
  196: 
  197: /*
  198:  * Return a pointer to the filename part of a path
  199:  */
  200: static const char *
  201: _ftp_filename(const char *file)
  202: {
  203: 	char *s;
  204: 
  205: 	if ((s = strrchr(file, '/')) == NULL)
  206: 		return (file);
  207: 	else
  208: 		return (s + 1);
  209: }
  210: 
  211: /*
  212:  * Change working directory to the directory that contains the specified
  213:  * file.
  214:  */
  215: static int
  216: _ftp_cwd(conn_t *conn, const char *file)
  217: {
  218: 	char *s;
  219: 	int e;
  220: 
  221: 	if ((s = strrchr(file, '/')) == NULL || s == file) {
  222: 		e = _ftp_cmd(conn, "CWD /");
  223: 	} else {
  224: 		e = _ftp_cmd(conn, "CWD %.*s", s - file, file);
  225: 	}
  226: 	if (e != FTP_FILE_ACTION_OK) {
  227: 		_ftp_seterr(e);
  228: 		return (-1);
  229: 	}
  230: 	return (0);
  231: }
  232: 
  233: /*
  234:  * Request and parse file stats
  235:  */
  236: static int
  237: _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
  238: {
  239: 	char *ln;
  240: 	const char *s;
  241: 	struct tm tm;
  242: 	time_t t;
  243: 	int e;
  244: 
  245: 	us->size = -1;
  246: 	us->atime = us->mtime = 0;
  247: 
  248: 	if ((s = strrchr(file, '/')) == NULL)
  249: 		s = file;
  250: 	else
  251: 		++s;
  252: 
  253: 	if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) {
  254: 		_ftp_seterr(e);
  255: 		return (-1);
  256: 	}
  257: 	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
  258: 		/* nothing */ ;
  259: 	for (us->size = 0; *ln && isdigit(*ln); ln++)
  260: 		us->size = us->size * 10 + *ln - '0';
  261: 	if (*ln && !isspace(*ln)) {
  262: 		_ftp_seterr(FTP_PROTOCOL_ERROR);
  263: 		us->size = -1;
  264: 		return (-1);
  265: 	}
  266: 	if (us->size == 0)
  267: 		us->size = -1;
  268: 	DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));
  269: 
  270: 	if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) {
  271: 		_ftp_seterr(e);
  272: 		return (-1);
  273: 	}
  274: 	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
  275: 		/* nothing */ ;
  276: 	switch (strspn(ln, "0123456789")) {
  277: 	case 14:
  278: 		break;
  279: 	case 15:
  280: 		ln++;
  281: 		ln[0] = '2';
  282: 		ln[1] = '0';
  283: 		break;
  284: 	default:
  285: 		_ftp_seterr(FTP_PROTOCOL_ERROR);
  286: 		return (-1);
  287: 	}
  288: 	if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
  289: 	    &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
  290: 	    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
  291: 		_ftp_seterr(FTP_PROTOCOL_ERROR);
  292: 		return (-1);
  293: 	}
  294: 	tm.tm_mon--;
  295: 	tm.tm_year -= 1900;
  296: 	tm.tm_isdst = -1;
  297: 	t = timegm(&tm);
  298: 	if (t == (time_t)-1)
  299: 		t = time(NULL);
  300: 	us->mtime = t;
  301: 	us->atime = t;
  302: 	DEBUG(fprintf(stderr,
  303: 	    "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n",
  304: 	    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  305: 	    tm.tm_hour, tm.tm_min, tm.tm_sec));
  306: 	return (0);
  307: }
  308: 
  309: /*
  310:  * I/O functions for FTP
  311:  */
  312: struct ftpio {
  313: 	conn_t	*cconn;		/* Control connection */
  314: 	conn_t	*dconn;		/* Data connection */
  315: 	int	 dir;		/* Direction */
  316: 	int	 eof;		/* EOF reached */
  317: 	int	 err;		/* Error code */
  318: };
  319: 
  320: static int	 _ftp_readfn(void *, char *, int);
  321: static int	 _ftp_writefn(void *, const char *, int);
  322: static fpos_t	 _ftp_seekfn(void *, fpos_t, int);
  323: static int	 _ftp_closefn(void *);
  324: 
  325: static int
  326: _ftp_readfn(void *v, char *buf, int len)
  327: {
  328: 	struct ftpio *io;
  329: 	int r;
  330: 
  331: 	io = (struct ftpio *)v;
  332: 	if (io == NULL) {
  333: 		errno = EBADF;
  334: 		return (-1);
  335: 	}
  336: 	if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
  337: 		errno = EBADF;
  338: 		return (-1);
  339: 	}
  340: 	if (io->err) {
  341: 		errno = io->err;
  342: 		return (-1);
  343: 	}
  344: 	if (io->eof)
  345: 		return (0);
  346: 	r = _fetch_read(io->dconn, buf, len);
  347: 	if (r > 0)
  348: 		return (r);
  349: 	if (r == 0) {
  350: 		io->eof = 1;
  351: 		return (0);
  352: 	}
  353: 	if (errno != EINTR)
  354: 		io->err = errno;
  355: 	return (-1);
  356: }
  357: 
  358: static int
  359: _ftp_writefn(void *v, const char *buf, int len)
  360: {
  361: 	struct ftpio *io;
  362: 	int w;
  363: 
  364: 	io = (struct ftpio *)v;
  365: 	if (io == NULL) {
  366: 		errno = EBADF;
  367: 		return (-1);
  368: 	}
  369: 	if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
  370: 		errno = EBADF;
  371: 		return (-1);
  372: 	}
  373: 	if (io->err) {
  374: 		errno = io->err;
  375: 		return (-1);
  376: 	}
  377: 	w = _fetch_write(io->dconn, buf, len);
  378: 	if (w >= 0)
  379: 		return (w);
  380: 	if (errno != EINTR)
  381: 		io->err = errno;
  382: 	return (-1);
  383: }
  384: 
  385: static fpos_t
  386: _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
  387: {
  388: 	struct ftpio *io;
  389: 
  390: 	io = (struct ftpio *)v;
  391: 	if (io == NULL) {
  392: 		errno = EBADF;
  393: 		return (-1);
  394: 	}
  395: 	errno = ESPIPE;
  396: 	return (-1);
  397: }
  398: 
  399: static int
  400: _ftp_closefn(void *v)
  401: {
  402: 	struct ftpio *io;
  403: 	int r;
  404: 
  405: 	io = (struct ftpio *)v;
  406: 	if (io == NULL) {
  407: 		errno = EBADF;
  408: 		return (-1);
  409: 	}
  410: 	if (io->dir == -1)
  411: 		return (0);
  412: 	if (io->cconn == NULL || io->dconn == NULL) {
  413: 		errno = EBADF;
  414: 		return (-1);
  415: 	}
  416: 	_fetch_close(io->dconn);
  417: 	io->dir = -1;
  418: 	io->dconn = NULL;
  419: 	DEBUG(fprintf(stderr, "Waiting for final status\n"));
  420: 	r = _ftp_chkerr(io->cconn);
  421: 	if (io->cconn == cached_connection && io->cconn->ref == 1)
  422: 		cached_connection = NULL;
  423: 	_fetch_close(io->cconn);
  424: 	free(io);
  425: 	return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
  426: }
  427: 
  428: static FILE *
  429: _ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
  430: {
  431: 	struct ftpio *io;
  432: 	FILE *f;
  433: 
  434: 	if (cconn == NULL || dconn == NULL)
  435: 		return (NULL);
  436: 	if ((io = malloc(sizeof(*io))) == NULL)
  437: 		return (NULL);
  438: 	io->cconn = cconn;
  439: 	io->dconn = dconn;
  440: 	io->dir = mode;
  441: 	io->eof = io->err = 0;
  442: 	f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
  443: 	if (f == NULL)
  444: 		free(io);
  445: 	return (f);
  446: }
  447: 
  448: /*
  449:  * Transfer file
  450:  */
  451: static FILE *
  452: _ftp_transfer(conn_t *conn, const char *oper, const char *file,
  453:     int mode, off_t offset, const char *flags)
  454: {
  455: 	struct sockaddr_storage sa;
  456: 	struct sockaddr_in6 *sin6;
  457: 	struct sockaddr_in *sin4;
  458: 	int low, pasv, verbose;
  459: 	int e, sd = -1;
  460: 	socklen_t l;
  461: 	char *s;
  462: 	FILE *df;
  463: 
  464: 	/* check flags */
  465: 	low = CHECK_FLAG('l');
  466: 	pasv = CHECK_FLAG('p');
  467: 	verbose = CHECK_FLAG('v');
  468: 
  469: 	/* passive mode */
  470: 	if (!pasv)
  471: 		pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
  472: 		    strncasecmp(s, "no", 2) != 0);
  473: 
  474: 	/* find our own address, bind, and listen */
  475: 	l = sizeof(sa);
  476: 	if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1)
  477: 		goto sysouch;
  478: 	if (sa.ss_family == AF_INET6)
  479: 		unmappedaddr((struct sockaddr_in6 *)&sa);
  480: 
  481: 	/* open data socket */
  482: 	if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
  483: 		_fetch_syserr();
  484: 		return (NULL);
  485: 	}
  486: 
  487: 	if (pasv) {
  488: 		u_char addr[64];
  489: 		char *ln, *p;
  490: 		unsigned int i;
  491: 		int port;
  492: 
  493: 		/* send PASV command */
  494: 		if (verbose)
  495: 			_fetch_info("setting passive mode");
  496: 		switch (sa.ss_family) {
  497: 		case AF_INET:
  498: 			if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
  499: 				goto ouch;
  500: 			break;
  501: 		case AF_INET6:
  502: 			if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
  503: 				if (e == -1)
  504: 					goto ouch;
  505: 				if ((e = _ftp_cmd(conn, "LPSV")) !=
  506: 				    FTP_LPASSIVE_MODE)
  507: 					goto ouch;
  508: 			}
  509: 			break;
  510: 		default:
  511: 			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
  512: 			goto ouch;
  513: 		}
  514: 
  515: 		/*
  516: 		 * Find address and port number. The reply to the PASV command
  517: 		 * is IMHO the one and only weak point in the FTP protocol.
  518: 		 */
  519: 		ln = conn->buf;
  520: 		switch (e) {
  521: 		case FTP_PASSIVE_MODE:
  522: 		case FTP_LPASSIVE_MODE:
  523: 			for (p = ln + 3; *p && !isdigit(*p); p++)
  524: 				/* nothing */ ;
  525: 			if (!*p) {
  526: 				e = FTP_PROTOCOL_ERROR;
  527: 				goto ouch;
  528: 			}
  529: 			l = (e == FTP_PASSIVE_MODE ? 6 : 21);
  530: 			for (i = 0; *p && i < l; i++, p++)
  531: 				addr[i] = strtol(p, &p, 10);
  532: 			if (i < l) {
  533: 				e = FTP_PROTOCOL_ERROR;
  534: 				goto ouch;
  535: 			}
  536: 			break;
  537: 		case FTP_EPASSIVE_MODE:
  538: 			for (p = ln + 3; *p && *p != '('; p++)
  539: 				/* nothing */ ;
  540: 			if (!*p) {
  541: 				e = FTP_PROTOCOL_ERROR;
  542: 				goto ouch;
  543: 			}
  544: 			++p;
  545: 			if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
  546: 				&port, &addr[3]) != 5 ||
  547: 			    addr[0] != addr[1] ||
  548: 			    addr[0] != addr[2] || addr[0] != addr[3]) {
  549: 				e = FTP_PROTOCOL_ERROR;
  550: 				goto ouch;
  551: 			}
  552: 			break;
  553: 		}
  554: 
  555: 		/* seek to required offset */
  556: 		if (offset)
  557: 			if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
  558: 				goto sysouch;
  559: 
  560: 		/* construct sockaddr for data socket */
  561: 		l = sizeof(sa);
  562: 		if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1)
  563: 			goto sysouch;
  564: 		if (sa.ss_family == AF_INET6)
  565: 			unmappedaddr((struct sockaddr_in6 *)&sa);
  566: 		switch (sa.ss_family) {
  567: 		case AF_INET6:
  568: 			sin6 = (struct sockaddr_in6 *)&sa;
  569: 			if (e == FTP_EPASSIVE_MODE)
  570: 				sin6->sin6_port = htons(port);
  571: 			else {
  572: 				bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
  573: 				bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
  574: 			}
  575: 			break;
  576: 		case AF_INET:
  577: 			sin4 = (struct sockaddr_in *)&sa;
  578: 			if (e == FTP_EPASSIVE_MODE)
  579: 				sin4->sin_port = htons(port);
  580: 			else {
  581: 				bcopy(addr, (char *)&sin4->sin_addr, 4);
  582: 				bcopy(addr + 4, (char *)&sin4->sin_port, 2);
  583: 			}
  584: 			break;
  585: 		default:
  586: 			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
  587: 			break;
  588: 		}
  589: 
  590: 		/* connect to data port */
  591: 		if (verbose)
  592: 			_fetch_info("opening data connection");
  593: 		if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
  594: 			goto sysouch;
  595: 
  596: 		/* make the server initiate the transfer */
  597: 		if (verbose)
  598: 			_fetch_info("initiating transfer");
  599: 		e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
  600: 		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
  601: 			goto ouch;
  602: 
  603: 	} else {
  604: 		u_int32_t a;
  605: 		u_short p;
  606: 		int arg, d;
  607: 		char *ap;
  608: 		char hname[INET6_ADDRSTRLEN];
  609: 
  610: 		switch (sa.ss_family) {
  611: 		case AF_INET6:
  612: 			((struct sockaddr_in6 *)&sa)->sin6_port = 0;
  613: #ifdef IPV6_PORTRANGE
  614: 			arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
  615: 			if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
  616: 				(char *)&arg, sizeof(arg)) == -1)
  617: 				goto sysouch;
  618: #endif
  619: 			break;
  620: 		case AF_INET:
  621: 			((struct sockaddr_in *)&sa)->sin_port = 0;
  622: 			arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
  623: 			if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
  624: 				(char *)&arg, sizeof(arg)) == -1)
  625: 				goto sysouch;
  626: 			break;
  627: 		}
  628: 		if (verbose)
  629: 			_fetch_info("binding data socket");
  630: 		if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
  631: 			goto sysouch;
  632: 		if (listen(sd, 1) == -1)
  633: 			goto sysouch;
  634: 
  635: 		/* find what port we're on and tell the server */
  636: 		if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1)
  637: 			goto sysouch;
  638: 		switch (sa.ss_family) {
  639: 		case AF_INET:
  640: 			sin4 = (struct sockaddr_in *)&sa;
  641: 			a = ntohl(sin4->sin_addr.s_addr);
  642: 			p = ntohs(sin4->sin_port);
  643: 			e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
  644: 			    (a >> 24) & 0xff, (a >> 16) & 0xff,
  645: 			    (a >> 8) & 0xff, a & 0xff,
  646: 			    (p >> 8) & 0xff, p & 0xff);
  647: 			break;
  648: 		case AF_INET6:
  649: #define UC(b)	(((int)b)&0xff)
  650: 			e = -1;
  651: 			sin6 = (struct sockaddr_in6 *)&sa;
  652: 			sin6->sin6_scope_id = 0;
  653: 			if (getnameinfo((struct sockaddr *)&sa, sa.ss_len,
  654: 				hname, sizeof(hname),
  655: 				NULL, 0, NI_NUMERICHOST) == 0) {
  656: 				e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
  657: 				    htons(sin6->sin6_port));
  658: 				if (e == -1)
  659: 					goto ouch;
  660: 			}
  661: 			if (e != FTP_OK) {
  662: 				ap = (char *)&sin6->sin6_addr;
  663: 				e = _ftp_cmd(conn,
  664: 				    "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
  665: 				    6, 16,
  666: 				    UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
  667: 				    UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
  668: 				    UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
  669: 				    UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
  670: 				    2,
  671: 				    (ntohs(sin6->sin6_port) >> 8) & 0xff,
  672: 				    ntohs(sin6->sin6_port)        & 0xff);
  673: 			}
  674: 			break;
  675: 		default:
  676: 			e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
  677: 			goto ouch;
  678: 		}
  679: 		if (e != FTP_OK)
  680: 			goto ouch;
  681: 
  682: 		/* seek to required offset */
  683: 		if (offset)
  684: 			if (_ftp_cmd(conn, "REST %llu",
  685: 			    (unsigned long long)offset) != FTP_FILE_OK)
  686: 				goto sysouch;
  687: 
  688: 		/* make the server initiate the transfer */
  689: 		if (verbose)
  690: 			_fetch_info("initiating transfer");
  691: 		e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
  692: 		if (e != FTP_OPEN_DATA_CONNECTION)
  693: 			goto ouch;
  694: 
  695: 		/* accept the incoming connection and go to town */
  696: 		if ((d = accept(sd, NULL, NULL)) == -1)
  697: 			goto sysouch;
  698: 		close(sd);
  699: 		sd = d;
  700: 	}
  701: 
  702: 	if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL)
  703: 		goto sysouch;
  704: 	return (df);
  705: 
  706: sysouch:
  707: 	_fetch_syserr();
  708: 	if (sd >= 0)
  709: 		close(sd);
  710: 	return (NULL);
  711: 
  712: ouch:
  713: 	if (e != -1)
  714: 		_ftp_seterr(e);
  715: 	if (sd >= 0)
  716: 		close(sd);
  717: 	return (NULL);
  718: }
  719: 
  720: /*
  721:  * Authenticate
  722:  */
  723: static int
  724: _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
  725: {
  726: 	const char *user, *pwd, *logname;
  727: 	char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
  728: 	int e, len;
  729: 
  730: 	/* XXX FTP_AUTH, and maybe .netrc */
  731: 
  732: 	/* send user name and password */
  733: 	if (url->user[0] == '\0')
  734: 		_fetch_netrc_auth(url);
  735: 	user = url->user;
  736: 	if (*user == '\0')
  737: 		user = getenv("FTP_LOGIN");
  738: 	if (user == NULL || *user == '\0')
  739: 		user = FTP_ANONYMOUS_USER;
  740: 	if (purl && url->port == _fetch_default_port(url->scheme))
  741: 		e = _ftp_cmd(conn, "USER %s@%s", user, url->host);
  742: 	else if (purl)
  743: 		e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
  744: 	else
  745: 		e = _ftp_cmd(conn, "USER %s", user);
  746: 
  747: 	/* did the server request a password? */
  748: 	if (e == FTP_NEED_PASSWORD) {
  749: 		pwd = url->pwd;
  750: 		if (*pwd == '\0')
  751: 			pwd = getenv("FTP_PASSWORD");
  752: 		if (pwd == NULL || *pwd == '\0') {
  753: 			if ((logname = getlogin()) == 0)
  754: 				logname = FTP_ANONYMOUS_USER;
  755: 			if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0)
  756: 				len = 0;
  757: 			else if (len > MAXLOGNAME)
  758: 				len = MAXLOGNAME;
  759: 			gethostname(pbuf + len, sizeof(pbuf) - len);
  760: 			pwd = pbuf;
  761: 		}
  762: 		e = _ftp_cmd(conn, "PASS %s", pwd);
  763: 	}
  764: 
  765: 	return (e);
  766: }
  767: 
  768: /*
  769:  * Log on to FTP server
  770:  */
  771: static conn_t *
  772: _ftp_connect(struct url *url, struct url *purl, const char *flags)
  773: {
  774: 	conn_t *conn;
  775: 	int e, direct, verbose;
  776: #ifdef INET6
  777: 	int af = AF_UNSPEC;
  778: #else
  779: 	int af = AF_INET;
  780: #endif
  781: 
  782: 	direct = CHECK_FLAG('d');
  783: 	verbose = CHECK_FLAG('v');
  784: 	if (CHECK_FLAG('4'))
  785: 		af = AF_INET;
  786: 	else if (CHECK_FLAG('6'))
  787: 		af = AF_INET6;
  788: 
  789: 	if (direct)
  790: 		purl = NULL;
  791: 
  792: 	/* check for proxy */
  793: 	if (purl) {
  794: 		/* XXX proxy authentication! */
  795: 		conn = _fetch_connect(purl->host, purl->port, af, verbose);
  796: 	} else {
  797: 		/* no proxy, go straight to target */
  798: 		conn = _fetch_connect(url->host, url->port, af, verbose);
  799: 		purl = NULL;
  800: 	}
  801: 
  802: 	/* check connection */
  803: 	if (conn == NULL)
  804: 		/* _fetch_connect() has already set an error code */
  805: 		return (NULL);
  806: 
  807: 	/* expect welcome message */
  808: 	if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY)
  809: 		goto fouch;
  810: 
  811: 	/* authenticate */
  812: 	if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
  813: 		goto fouch;
  814: 
  815: 	/* might as well select mode and type at once */
  816: #ifdef FTP_FORCE_STREAM_MODE
  817: 	if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */
  818: 		goto fouch;
  819: #endif
  820: 	if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */
  821: 		goto fouch;
  822: 
  823: 	/* done */
  824: 	return (conn);
  825: 
  826: fouch:
  827: 	if (e != -1)
  828: 		_ftp_seterr(e);
  829: 	_fetch_close(conn);
  830: 	return (NULL);
  831: }
  832: 
  833: /*
  834:  * Disconnect from server
  835:  */
  836: static void
  837: _ftp_disconnect(conn_t *conn)
  838: {
  839: 	(void)_ftp_cmd(conn, "QUIT");
  840: 	if (conn == cached_connection && conn->ref == 1)
  841: 		cached_connection = NULL;
  842: 	_fetch_close(conn);
  843: }
  844: 
  845: /*
  846:  * Check if we're already connected
  847:  */
  848: static int
  849: _ftp_isconnected(struct url *url)
  850: {
  851: 	return (cached_connection
  852: 	    && (strcmp(url->host, cached_host.host) == 0)
  853: 	    && (strcmp(url->user, cached_host.user) == 0)
  854: 	    && (strcmp(url->pwd, cached_host.pwd) == 0)
  855: 	    && (url->port == cached_host.port));
  856: }
  857: 
  858: /*
  859:  * Check the cache, reconnect if no luck
  860:  */
  861: static conn_t *
  862: _ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
  863: {
  864: 	conn_t *conn;
  865: 	int e;
  866: 
  867: 	/* set default port */
  868: 	if (!url->port)
  869: 		url->port = _fetch_default_port(url->scheme);
  870: 
  871: 	/* try to use previously cached connection */
  872: 	if (_ftp_isconnected(url)) {
  873: 		e = _ftp_cmd(cached_connection, "NOOP");
  874: 		if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
  875: 			return (_fetch_ref(cached_connection));
  876: 	}
  877: 
  878: 	/* connect to server */
  879: 	if ((conn = _ftp_connect(url, purl, flags)) == NULL)
  880: 		return (NULL);
  881: 	if (cached_connection)
  882: 		_ftp_disconnect(cached_connection);
  883: 	cached_connection = _fetch_ref(conn);
  884: 	memcpy(&cached_host, url, sizeof(*url));
  885: 	return (conn);
  886: }
  887: 
  888: /*
  889:  * Check the proxy settings
  890:  */
  891: static struct url *
  892: _ftp_get_proxy(const char *flags)
  893: {
  894: 	struct url *purl;
  895: 	char *p;
  896: 
  897: 	if (flags != NULL && strchr(flags, 'd') != NULL)
  898: 		return (NULL);
  899: 	if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
  900: 		(p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
  901: 	    *p && (purl = fetchParseURL(p)) != NULL) {
  902: 		if (!*purl->scheme) {
  903: 			if (getenv("FTP_PROXY") || getenv("ftp_proxy"))
  904: 				strcpy(purl->scheme, SCHEME_FTP);
  905: 			else
  906: 				strcpy(purl->scheme, SCHEME_HTTP);
  907: 		}
  908: 		if (!purl->port)
  909: 			purl->port = _fetch_default_proxy_port(purl->scheme);
  910: 		if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
  911: 		    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
  912: 			return (purl);
  913: 		fetchFreeURL(purl);
  914: 	}
  915: 	return (NULL);
  916: }
  917: 
  918: /*
  919:  * Process an FTP request
  920:  */
  921: FILE *
  922: _ftp_request(struct url *url, const char *op, struct url_stat *us,
  923:     struct url *purl, const char *flags)
  924: {
  925: 	conn_t *conn;
  926: 	int oflag;
  927: 
  928: 	/* check if we should use HTTP instead */
  929: 	if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
  930: 		if (strcmp(op, "STAT") == 0)
  931: 			return (_http_request(url, "HEAD", us, purl, flags));
  932: 		else if (strcmp(op, "RETR") == 0)
  933: 			return (_http_request(url, "GET", us, purl, flags));
  934: 		/*
  935: 		 * Our HTTP code doesn't support PUT requests yet, so try
  936: 		 * a direct connection.
  937: 		 */
  938: 	}
  939: 
  940: 	/* connect to server */
  941: 	conn = _ftp_cached_connect(url, purl, flags);
  942: 	if (purl)
  943: 		fetchFreeURL(purl);
  944: 	if (conn == NULL)
  945: 		return (NULL);
  946: 
  947: 	/* change directory */
  948: 	if (_ftp_cwd(conn, url->doc) == -1)
  949: 		return (NULL);
  950: 
  951: 	/* stat file */
  952: 	if (us && _ftp_stat(conn, url->doc, us) == -1
  953: 	    && fetchLastErrCode != FETCH_PROTO
  954: 	    && fetchLastErrCode != FETCH_UNAVAIL)
  955: 		return (NULL);
  956: 
  957: 	/* just a stat */
  958: 	if (strcmp(op, "STAT") == 0)
  959: 		return (FILE *)1; /* bogus return value */
  960: 	if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)
  961: 		oflag = O_WRONLY;
  962: 	else
  963: 		oflag = O_RDONLY;
  964: 
  965: 	/* initiate the transfer */
  966: 	return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
  967: }
  968: 
  969: /*
  970:  * Get and stat file
  971:  */
  972: FILE *
  973: fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
  974: {
  975: 	return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags));
  976: }
  977: 
  978: /*
  979:  * Get file
  980:  */
  981: FILE *
  982: fetchGetFTP(struct url *url, const char *flags)
  983: {
  984: 	return (fetchXGetFTP(url, NULL, flags));
  985: }
  986: 
  987: /*
  988:  * Put file
  989:  */
  990: FILE *
  991: fetchPutFTP(struct url *url, const char *flags)
  992: {
  993: 
  994: 	return (_ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
  995: 	    _ftp_get_proxy(flags), flags));
  996: }
  997: 
  998: /*
  999:  * Get file stats
 1000:  */
 1001: int
 1002: fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
 1003: {
 1004: 	FILE *f;
 1005: 
 1006: 	f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags);
 1007: 	if (f == NULL)
 1008: 		return (-1);
 1009: 	return (0);
 1010: }
 1011: 
 1012: /*
 1013:  * List a directory
 1014:  */
 1015: struct url_ent *
 1016: fetchListFTP(struct url *url __unused, const char *flags __unused)
 1017: {
 1018: 	warnx("fetchListFTP(): not implemented");
 1019: 	return (NULL);
 1020: }