xref: /dflybsd-src/lib/libftpio/ftpio.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * ----------------------------------------------------------------------------
3*86d7f5d3SJohn Marino  * "THE BEER-WARE LICENSE" (Revision 42):
4*86d7f5d3SJohn Marino  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
5*86d7f5d3SJohn Marino  * can do whatever you want with this stuff. If we meet some day, and you think
6*86d7f5d3SJohn Marino  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7*86d7f5d3SJohn Marino  * ----------------------------------------------------------------------------
8*86d7f5d3SJohn Marino  *
9*86d7f5d3SJohn Marino  * Major Changelog:
10*86d7f5d3SJohn Marino  *
11*86d7f5d3SJohn Marino  * Jordan K. Hubbard
12*86d7f5d3SJohn Marino  * 17 Jan 1996
13*86d7f5d3SJohn Marino  *
14*86d7f5d3SJohn Marino  * Turned inside out. Now returns xfers as new file ids, not as a special
15*86d7f5d3SJohn Marino  * `state' of FTP_t
16*86d7f5d3SJohn Marino  *
17*86d7f5d3SJohn Marino  * $FreeBSD: src/lib/libftpio/ftpio.c,v 1.33.2.4 2002/07/25 15:25:32 ume Exp $
18*86d7f5d3SJohn Marino  * $DragonFly: src/lib/libftpio/ftpio.c,v 1.8 2005/07/23 20:23:06 joerg Exp $
19*86d7f5d3SJohn Marino  *
20*86d7f5d3SJohn Marino  */
21*86d7f5d3SJohn Marino 
22*86d7f5d3SJohn Marino #include <sys/param.h>
23*86d7f5d3SJohn Marino #include <sys/socket.h>
24*86d7f5d3SJohn Marino 
25*86d7f5d3SJohn Marino #include <netinet/in.h>
26*86d7f5d3SJohn Marino 
27*86d7f5d3SJohn Marino #include <arpa/inet.h>
28*86d7f5d3SJohn Marino 
29*86d7f5d3SJohn Marino #include <ctype.h>
30*86d7f5d3SJohn Marino #include <errno.h>
31*86d7f5d3SJohn Marino #include <ftpio.h>
32*86d7f5d3SJohn Marino #include <inttypes.h>
33*86d7f5d3SJohn Marino #include <netdb.h>
34*86d7f5d3SJohn Marino #include <signal.h>
35*86d7f5d3SJohn Marino #include <stdarg.h>
36*86d7f5d3SJohn Marino #include <stdio.h>
37*86d7f5d3SJohn Marino #include <stdlib.h>
38*86d7f5d3SJohn Marino #include <string.h>
39*86d7f5d3SJohn Marino #include <unistd.h>
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino #define SUCCESS		 0
42*86d7f5d3SJohn Marino #define FAILURE		-1
43*86d7f5d3SJohn Marino 
44*86d7f5d3SJohn Marino #ifndef TRUE
45*86d7f5d3SJohn Marino #define TRUE	(1)
46*86d7f5d3SJohn Marino #define FALSE	(0)
47*86d7f5d3SJohn Marino #endif
48*86d7f5d3SJohn Marino 
49*86d7f5d3SJohn Marino /* How to see by a given code whether or not the connection has timed out */
50*86d7f5d3SJohn Marino #define FTP_TIMEOUT(code)	(FtpTimedOut || code == FTP_TIMED_OUT)
51*86d7f5d3SJohn Marino 
52*86d7f5d3SJohn Marino /* Internal routines - deal only with internal FTP_t type */
53*86d7f5d3SJohn Marino static FTP_t	ftp_new(void);
54*86d7f5d3SJohn Marino static void	check_passive(FILE *fp);
55*86d7f5d3SJohn Marino static int	ftp_read_method(void *n, char *buf, int nbytes);
56*86d7f5d3SJohn Marino static int	ftp_write_method(void *n, const char *buf, int nbytes);
57*86d7f5d3SJohn Marino static int	ftp_close_method(void *n);
58*86d7f5d3SJohn Marino static int	writes(int fd, const char *s);
59*86d7f5d3SJohn Marino static char	*get_a_line(FTP_t ftp);
60*86d7f5d3SJohn Marino static int	get_a_number(FTP_t ftp, char **q);
61*86d7f5d3SJohn Marino static int	botch(const char *func, const char *botch_state);
62*86d7f5d3SJohn Marino static int	cmd(FTP_t ftp, const char *fmt, ...) __printflike(2, 3);
63*86d7f5d3SJohn Marino static int	ftp_login_session(FTP_t ftp, const char *host, int af,
64*86d7f5d3SJohn Marino 				  const char *user, const char *passwd,
65*86d7f5d3SJohn Marino 				  int port, int verbose);
66*86d7f5d3SJohn Marino static int	ftp_file_op(FTP_t ftp, const char *operation, const char *file,
67*86d7f5d3SJohn Marino 			    FILE **fp, const char *mode, off_t *seekto);
68*86d7f5d3SJohn Marino static int	ftp_close(FTP_t ftp);
69*86d7f5d3SJohn Marino static int	get_url_info(const char *url_in, char *host_ret,
70*86d7f5d3SJohn Marino 			     size_t host_len, int *port_ret,
71*86d7f5d3SJohn Marino 			     char *name_ret, size_t name_len);
72*86d7f5d3SJohn Marino static void	ftp_timeout(int sig);
73*86d7f5d3SJohn Marino static void	ftp_set_timeout(void);
74*86d7f5d3SJohn Marino static void	ftp_clear_timeout(void);
75*86d7f5d3SJohn Marino static void	ai_unmapped(struct addrinfo *);
76*86d7f5d3SJohn Marino 
77*86d7f5d3SJohn Marino int		networkInit(void);
78*86d7f5d3SJohn Marino 
79*86d7f5d3SJohn Marino 
80*86d7f5d3SJohn Marino /* Global status variable - ick */
81*86d7f5d3SJohn Marino int FtpTimedOut;
82*86d7f5d3SJohn Marino 
83*86d7f5d3SJohn Marino /* FTP happy status codes */
84*86d7f5d3SJohn Marino #define FTP_GENERALLY_HAPPY	200
85*86d7f5d3SJohn Marino #define FTP_ASCII_HAPPY		FTP_GENERALLY_HAPPY
86*86d7f5d3SJohn Marino #define FTP_BINARY_HAPPY	FTP_GENERALLY_HAPPY
87*86d7f5d3SJohn Marino #define FTP_PORT_HAPPY		FTP_GENERALLY_HAPPY
88*86d7f5d3SJohn Marino #define FTP_HAPPY_COMMENT	220
89*86d7f5d3SJohn Marino #define FTP_QUIT_HAPPY		221
90*86d7f5d3SJohn Marino #define FTP_TRANSFER_HAPPY	226
91*86d7f5d3SJohn Marino #define FTP_PASSIVE_HAPPY	227
92*86d7f5d3SJohn Marino #define FTP_LPASSIVE_HAPPY	228
93*86d7f5d3SJohn Marino #define FTP_EPASSIVE_HAPPY	229
94*86d7f5d3SJohn Marino #define FTP_CHDIR_HAPPY		250
95*86d7f5d3SJohn Marino 
96*86d7f5d3SJohn Marino /* FTP unhappy status codes */
97*86d7f5d3SJohn Marino #define FTP_TIMED_OUT		421
98*86d7f5d3SJohn Marino 
99*86d7f5d3SJohn Marino /* Placeholder in case we want to do any pre-init stuff at some point */
100*86d7f5d3SJohn Marino int
networkInit(void)101*86d7f5d3SJohn Marino networkInit(void)
102*86d7f5d3SJohn Marino {
103*86d7f5d3SJohn Marino     return SUCCESS;	/* XXX dummy function for now XXX */
104*86d7f5d3SJohn Marino }
105*86d7f5d3SJohn Marino 
106*86d7f5d3SJohn Marino /* Check a return code with some lenience for back-dated garbage that might be in the buffer */
107*86d7f5d3SJohn Marino static int
check_code(FTP_t ftp,int var,int preferred)108*86d7f5d3SJohn Marino check_code(FTP_t ftp, int var, int preferred)
109*86d7f5d3SJohn Marino {
110*86d7f5d3SJohn Marino     ftp->error = 0;
111*86d7f5d3SJohn Marino     while (1) {
112*86d7f5d3SJohn Marino 	if (var == preferred)
113*86d7f5d3SJohn Marino 	    return 0;
114*86d7f5d3SJohn Marino 	else if (var == FTP_TRANSFER_HAPPY)	/* last operation succeeded */
115*86d7f5d3SJohn Marino 	    var = get_a_number(ftp, NULL);
116*86d7f5d3SJohn Marino 	else if (var == FTP_HAPPY_COMMENT)	/* chit-chat */
117*86d7f5d3SJohn Marino 	    var = get_a_number(ftp, NULL);
118*86d7f5d3SJohn Marino 	else if (var == FTP_GENERALLY_HAPPY)	/* general success code */
119*86d7f5d3SJohn Marino 	    var = get_a_number(ftp, NULL);
120*86d7f5d3SJohn Marino 	else {
121*86d7f5d3SJohn Marino 	    ftp->error = var;
122*86d7f5d3SJohn Marino 	    return 1;
123*86d7f5d3SJohn Marino 	}
124*86d7f5d3SJohn Marino     }
125*86d7f5d3SJohn Marino }
126*86d7f5d3SJohn Marino 
127*86d7f5d3SJohn Marino int
ftpAscii(FILE * fp)128*86d7f5d3SJohn Marino ftpAscii(FILE *fp)
129*86d7f5d3SJohn Marino {
130*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
131*86d7f5d3SJohn Marino     int i;
132*86d7f5d3SJohn Marino 
133*86d7f5d3SJohn Marino     if (!ftp->is_binary)
134*86d7f5d3SJohn Marino 	return SUCCESS;
135*86d7f5d3SJohn Marino     i = cmd(ftp, "TYPE A");
136*86d7f5d3SJohn Marino     if (i < 0 || check_code(ftp, i, FTP_ASCII_HAPPY))
137*86d7f5d3SJohn Marino 	return i;
138*86d7f5d3SJohn Marino     ftp->is_binary = FALSE;
139*86d7f5d3SJohn Marino     return SUCCESS;
140*86d7f5d3SJohn Marino }
141*86d7f5d3SJohn Marino 
142*86d7f5d3SJohn Marino int
ftpBinary(FILE * fp)143*86d7f5d3SJohn Marino ftpBinary(FILE *fp)
144*86d7f5d3SJohn Marino {
145*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
146*86d7f5d3SJohn Marino     int i;
147*86d7f5d3SJohn Marino 
148*86d7f5d3SJohn Marino     if (ftp->is_binary)
149*86d7f5d3SJohn Marino 	return SUCCESS;
150*86d7f5d3SJohn Marino     i = cmd(ftp, "TYPE I");
151*86d7f5d3SJohn Marino     if (i < 0 || check_code(ftp, i, FTP_BINARY_HAPPY))
152*86d7f5d3SJohn Marino 	return i;
153*86d7f5d3SJohn Marino     ftp->is_binary = TRUE;
154*86d7f5d3SJohn Marino     return SUCCESS;
155*86d7f5d3SJohn Marino }
156*86d7f5d3SJohn Marino void
ftpVerbose(FILE * fp,int status)157*86d7f5d3SJohn Marino ftpVerbose(FILE *fp, int status)
158*86d7f5d3SJohn Marino {
159*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
160*86d7f5d3SJohn Marino     ftp->is_verbose = status;
161*86d7f5d3SJohn Marino }
162*86d7f5d3SJohn Marino 
163*86d7f5d3SJohn Marino int
ftpChdir(FILE * fp,char * dir)164*86d7f5d3SJohn Marino ftpChdir(FILE *fp, char *dir)
165*86d7f5d3SJohn Marino {
166*86d7f5d3SJohn Marino     int i;
167*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
168*86d7f5d3SJohn Marino 
169*86d7f5d3SJohn Marino     i = cmd(ftp, "CWD %s", dir);
170*86d7f5d3SJohn Marino     if (i < 0 || check_code(ftp, i, FTP_CHDIR_HAPPY))
171*86d7f5d3SJohn Marino 	return i;
172*86d7f5d3SJohn Marino     return SUCCESS;
173*86d7f5d3SJohn Marino }
174*86d7f5d3SJohn Marino 
175*86d7f5d3SJohn Marino int
ftpErrno(FILE * fp)176*86d7f5d3SJohn Marino ftpErrno(FILE *fp)
177*86d7f5d3SJohn Marino {
178*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
179*86d7f5d3SJohn Marino     return ftp->error;
180*86d7f5d3SJohn Marino }
181*86d7f5d3SJohn Marino 
182*86d7f5d3SJohn Marino const char *
ftpErrString(int error)183*86d7f5d3SJohn Marino ftpErrString(int error)
184*86d7f5d3SJohn Marino {
185*86d7f5d3SJohn Marino     int	k;
186*86d7f5d3SJohn Marino 
187*86d7f5d3SJohn Marino     if (error == -1)
188*86d7f5d3SJohn Marino 	return("connection in wrong state");
189*86d7f5d3SJohn Marino     if (error < 100)
190*86d7f5d3SJohn Marino 	/* XXX soon UNIX errnos will catch up with FTP protocol errnos */
191*86d7f5d3SJohn Marino 	return strerror(error);
192*86d7f5d3SJohn Marino     for (k = 0; k < ftpErrListLength; k++)
193*86d7f5d3SJohn Marino       if (ftpErrList[k].num == error)
194*86d7f5d3SJohn Marino 	return(ftpErrList[k].string);
195*86d7f5d3SJohn Marino     return("Unknown error");
196*86d7f5d3SJohn Marino }
197*86d7f5d3SJohn Marino 
198*86d7f5d3SJohn Marino off_t
ftpGetSize(FILE * fp,const char * name)199*86d7f5d3SJohn Marino ftpGetSize(FILE *fp, const char *name)
200*86d7f5d3SJohn Marino {
201*86d7f5d3SJohn Marino     int i;
202*86d7f5d3SJohn Marino     char p[BUFSIZ], *cp, *ep;
203*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
204*86d7f5d3SJohn Marino     off_t size;
205*86d7f5d3SJohn Marino 
206*86d7f5d3SJohn Marino     check_passive(fp);
207*86d7f5d3SJohn Marino     if ((size_t)snprintf(p, sizeof(p), "SIZE %s\r\n", name) >= sizeof(p))
208*86d7f5d3SJohn Marino 	return (off_t)(-1);
209*86d7f5d3SJohn Marino     if (ftp->is_verbose)
210*86d7f5d3SJohn Marino 	fprintf(stderr, "Sending %s", p);
211*86d7f5d3SJohn Marino     if (writes(ftp->fd_ctrl, p))
212*86d7f5d3SJohn Marino 	return (off_t)(-1);
213*86d7f5d3SJohn Marino     i = get_a_number(ftp, &cp);
214*86d7f5d3SJohn Marino     if (check_code(ftp, i, 213))
215*86d7f5d3SJohn Marino 	return (off_t)(-1);
216*86d7f5d3SJohn Marino 
217*86d7f5d3SJohn Marino     errno = 0;				/* to check for ERANGE */
218*86d7f5d3SJohn Marino     size = (off_t)strtoq(cp, &ep, 10);
219*86d7f5d3SJohn Marino     if (*ep != '\0' || errno == ERANGE)
220*86d7f5d3SJohn Marino 	return (off_t)(-1);
221*86d7f5d3SJohn Marino     return size;
222*86d7f5d3SJohn Marino }
223*86d7f5d3SJohn Marino 
224*86d7f5d3SJohn Marino time_t
ftpGetModtime(FILE * fp,const char * name)225*86d7f5d3SJohn Marino ftpGetModtime(FILE *fp, const char *name)
226*86d7f5d3SJohn Marino {
227*86d7f5d3SJohn Marino     char p[BUFSIZ], *cp;
228*86d7f5d3SJohn Marino     struct tm t;
229*86d7f5d3SJohn Marino     time_t t0 = time (0);
230*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
231*86d7f5d3SJohn Marino     int i;
232*86d7f5d3SJohn Marino 
233*86d7f5d3SJohn Marino     check_passive(fp);
234*86d7f5d3SJohn Marino     if ((size_t)snprintf(p, sizeof(p), "MDTM %s\r\n", name) >= sizeof(p))
235*86d7f5d3SJohn Marino 	return (time_t)0;
236*86d7f5d3SJohn Marino     if (ftp->is_verbose)
237*86d7f5d3SJohn Marino 	fprintf(stderr, "Sending %s", p);
238*86d7f5d3SJohn Marino     if (writes(ftp->fd_ctrl, p))
239*86d7f5d3SJohn Marino 	return (time_t)0;
240*86d7f5d3SJohn Marino     i = get_a_number(ftp, &cp);
241*86d7f5d3SJohn Marino     if (check_code(ftp, i, 213))
242*86d7f5d3SJohn Marino 	return (time_t)0;
243*86d7f5d3SJohn Marino     while (*cp && !isdigit(*cp))
244*86d7f5d3SJohn Marino 	cp++;
245*86d7f5d3SJohn Marino     if (!*cp)
246*86d7f5d3SJohn Marino 	return (time_t)0;
247*86d7f5d3SJohn Marino     t0 = localtime (&t0)->tm_gmtoff;
248*86d7f5d3SJohn Marino     sscanf(cp, "%04d%02d%02d%02d%02d%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
249*86d7f5d3SJohn Marino     t.tm_mon--;
250*86d7f5d3SJohn Marino     t.tm_year -= 1900;
251*86d7f5d3SJohn Marino     t.tm_isdst=-1;
252*86d7f5d3SJohn Marino     t.tm_gmtoff = 0;
253*86d7f5d3SJohn Marino     t0 += mktime (&t);
254*86d7f5d3SJohn Marino     return t0;
255*86d7f5d3SJohn Marino }
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino FILE *
ftpGet(FILE * fp,const char * file,off_t * seekto)258*86d7f5d3SJohn Marino ftpGet(FILE *fp, const char *file, off_t *seekto)
259*86d7f5d3SJohn Marino {
260*86d7f5d3SJohn Marino     FILE *fp2;
261*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
262*86d7f5d3SJohn Marino 
263*86d7f5d3SJohn Marino     check_passive(fp);
264*86d7f5d3SJohn Marino     if (ftpBinary(fp) != SUCCESS)
265*86d7f5d3SJohn Marino 	return NULL;
266*86d7f5d3SJohn Marino 
267*86d7f5d3SJohn Marino     if (ftp_file_op(ftp, "RETR", file, &fp2, "r", seekto) == SUCCESS)
268*86d7f5d3SJohn Marino 	return fp2;
269*86d7f5d3SJohn Marino     return NULL;
270*86d7f5d3SJohn Marino }
271*86d7f5d3SJohn Marino 
272*86d7f5d3SJohn Marino /* Returns a standard FILE pointer type representing an open control connection */
273*86d7f5d3SJohn Marino FILE *
ftpLogin(const char * host,const char * user,const char * passwd,int port,int verbose,int * retcode)274*86d7f5d3SJohn Marino ftpLogin(const char *host, const char *user, const char *passwd, int port,
275*86d7f5d3SJohn Marino 	 int verbose, int *retcode)
276*86d7f5d3SJohn Marino {
277*86d7f5d3SJohn Marino #ifdef INET6
278*86d7f5d3SJohn Marino     return ftpLoginAf(host, AF_UNSPEC, user, passwd, port, verbose, retcode);
279*86d7f5d3SJohn Marino #else
280*86d7f5d3SJohn Marino     return ftpLoginAf(host, AF_INET, user, passwd, port, verbose, retcode);
281*86d7f5d3SJohn Marino #endif
282*86d7f5d3SJohn Marino }
283*86d7f5d3SJohn Marino 
284*86d7f5d3SJohn Marino FILE *
ftpLoginAf(const char * host,int af,const char * user,const char * passwd,int port,int verbose,int * retcode)285*86d7f5d3SJohn Marino ftpLoginAf(const char *host, int af, const char *user, const char *passwd,
286*86d7f5d3SJohn Marino 	   int port, int verbose, int *retcode)
287*86d7f5d3SJohn Marino {
288*86d7f5d3SJohn Marino     FTP_t n;
289*86d7f5d3SJohn Marino     FILE *fp;
290*86d7f5d3SJohn Marino 
291*86d7f5d3SJohn Marino     if (retcode)
292*86d7f5d3SJohn Marino 	*retcode = 0;
293*86d7f5d3SJohn Marino     if (networkInit() != SUCCESS)
294*86d7f5d3SJohn Marino 	return NULL;
295*86d7f5d3SJohn Marino 
296*86d7f5d3SJohn Marino     n = ftp_new();
297*86d7f5d3SJohn Marino     fp = NULL;
298*86d7f5d3SJohn Marino     if (n && ftp_login_session(n, host, af, user, passwd, port, verbose) == SUCCESS) {
299*86d7f5d3SJohn Marino 	fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method);	/* BSD 4.4 function! */
300*86d7f5d3SJohn Marino     }
301*86d7f5d3SJohn Marino     if (retcode) {
302*86d7f5d3SJohn Marino 	if (!n)
303*86d7f5d3SJohn Marino 	    *retcode = (FtpTimedOut ? FTP_TIMED_OUT : -1);
304*86d7f5d3SJohn Marino 	/* Poor attempt at mapping real errnos to FTP error codes */
305*86d7f5d3SJohn Marino 	else switch(n->error) {
306*86d7f5d3SJohn Marino 	    case EADDRNOTAVAIL:
307*86d7f5d3SJohn Marino 		*retcode = FTP_TIMED_OUT;	/* Actually no such host, but we have no way of saying that. :-( */
308*86d7f5d3SJohn Marino 		break;
309*86d7f5d3SJohn Marino 
310*86d7f5d3SJohn Marino             case ETIMEDOUT:
311*86d7f5d3SJohn Marino 		*retcode = FTP_TIMED_OUT;
312*86d7f5d3SJohn Marino 		break;
313*86d7f5d3SJohn Marino 
314*86d7f5d3SJohn Marino 	    default:
315*86d7f5d3SJohn Marino 		*retcode = n->error;
316*86d7f5d3SJohn Marino 		break;
317*86d7f5d3SJohn Marino 	}
318*86d7f5d3SJohn Marino     }
319*86d7f5d3SJohn Marino     return fp;
320*86d7f5d3SJohn Marino }
321*86d7f5d3SJohn Marino 
322*86d7f5d3SJohn Marino FILE *
ftpPut(FILE * fp,const char * file)323*86d7f5d3SJohn Marino ftpPut(FILE *fp, const char *file)
324*86d7f5d3SJohn Marino {
325*86d7f5d3SJohn Marino     FILE *fp2;
326*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
327*86d7f5d3SJohn Marino 
328*86d7f5d3SJohn Marino     check_passive(fp);
329*86d7f5d3SJohn Marino     if (ftp_file_op(ftp, "STOR", file, &fp2, "w", NULL) == SUCCESS)
330*86d7f5d3SJohn Marino 	return fp2;
331*86d7f5d3SJohn Marino     return NULL;
332*86d7f5d3SJohn Marino }
333*86d7f5d3SJohn Marino 
334*86d7f5d3SJohn Marino int
ftpPassive(FILE * fp,int st)335*86d7f5d3SJohn Marino ftpPassive(FILE *fp, int st)
336*86d7f5d3SJohn Marino {
337*86d7f5d3SJohn Marino     FTP_t ftp = fcookie(fp);
338*86d7f5d3SJohn Marino 
339*86d7f5d3SJohn Marino     ftp->is_passive = !!st;	/* normalize "st" to zero or one */
340*86d7f5d3SJohn Marino     return SUCCESS;
341*86d7f5d3SJohn Marino }
342*86d7f5d3SJohn Marino 
343*86d7f5d3SJohn Marino FILE *
ftpGetURL(const char * url,const char * user,const char * passwd,int * retcode)344*86d7f5d3SJohn Marino ftpGetURL(const char *url, const char *user, const char *passwd, int *retcode)
345*86d7f5d3SJohn Marino {
346*86d7f5d3SJohn Marino #ifdef INET6
347*86d7f5d3SJohn Marino     return ftpGetURLAf(url, AF_UNSPEC, user, passwd, retcode);
348*86d7f5d3SJohn Marino #else
349*86d7f5d3SJohn Marino     return ftpGetURLAf(url, AF_INET, user, passwd, retcode);
350*86d7f5d3SJohn Marino #endif
351*86d7f5d3SJohn Marino }
352*86d7f5d3SJohn Marino 
353*86d7f5d3SJohn Marino FILE *
ftpGetURLAf(const char * url,int af,const char * user,const char * passwd,int * retcode)354*86d7f5d3SJohn Marino ftpGetURLAf(const char *url, int af, const char *user, const char *passwd,
355*86d7f5d3SJohn Marino 	    int *retcode)
356*86d7f5d3SJohn Marino {
357*86d7f5d3SJohn Marino     char host[255], name[255];
358*86d7f5d3SJohn Marino     int port;
359*86d7f5d3SJohn Marino     FILE *fp2;
360*86d7f5d3SJohn Marino     static FILE *fp = NULL;
361*86d7f5d3SJohn Marino     static char *prev_host;
362*86d7f5d3SJohn Marino 
363*86d7f5d3SJohn Marino     if (retcode)
364*86d7f5d3SJohn Marino 	*retcode = 0;
365*86d7f5d3SJohn Marino     if (get_url_info(url, host, sizeof(host), &port, name,
366*86d7f5d3SJohn Marino 		     sizeof(name)) == SUCCESS) {
367*86d7f5d3SJohn Marino 	if (fp && prev_host) {
368*86d7f5d3SJohn Marino 	    if (!strcmp(prev_host, host)) {
369*86d7f5d3SJohn Marino 		/* Try to use cached connection */
370*86d7f5d3SJohn Marino 		fp2 = ftpGet(fp, name, NULL);
371*86d7f5d3SJohn Marino 		if (!fp2) {
372*86d7f5d3SJohn Marino 		    /* Connection timed out or was no longer valid */
373*86d7f5d3SJohn Marino 		    fclose(fp);
374*86d7f5d3SJohn Marino 		    free(prev_host);
375*86d7f5d3SJohn Marino 		    prev_host = NULL;
376*86d7f5d3SJohn Marino 		}
377*86d7f5d3SJohn Marino 		else
378*86d7f5d3SJohn Marino 		    return fp2;
379*86d7f5d3SJohn Marino 	    }
380*86d7f5d3SJohn Marino 	    else {
381*86d7f5d3SJohn Marino 		/* It's a different host now, flush old */
382*86d7f5d3SJohn Marino 		fclose(fp);
383*86d7f5d3SJohn Marino 		free(prev_host);
384*86d7f5d3SJohn Marino 		prev_host = NULL;
385*86d7f5d3SJohn Marino 	    }
386*86d7f5d3SJohn Marino 	}
387*86d7f5d3SJohn Marino 	fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode);
388*86d7f5d3SJohn Marino 	if (fp) {
389*86d7f5d3SJohn Marino 	    fp2 = ftpGet(fp, name, NULL);
390*86d7f5d3SJohn Marino 	    if (!fp2) {
391*86d7f5d3SJohn Marino 		/* Connection timed out or was no longer valid */
392*86d7f5d3SJohn Marino 		if (retcode)
393*86d7f5d3SJohn Marino 		    *retcode = ftpErrno(fp);
394*86d7f5d3SJohn Marino 		fclose(fp);
395*86d7f5d3SJohn Marino 		fp = NULL;
396*86d7f5d3SJohn Marino 	    }
397*86d7f5d3SJohn Marino 	    else
398*86d7f5d3SJohn Marino 		prev_host = strdup(host);
399*86d7f5d3SJohn Marino 	    return fp2;
400*86d7f5d3SJohn Marino 	}
401*86d7f5d3SJohn Marino     }
402*86d7f5d3SJohn Marino     return NULL;
403*86d7f5d3SJohn Marino }
404*86d7f5d3SJohn Marino 
405*86d7f5d3SJohn Marino FILE *
ftpPutURL(const char * url,const char * user,const char * passwd,int * retcode)406*86d7f5d3SJohn Marino ftpPutURL(const char *url, const char *user, const char *passwd, int *retcode)
407*86d7f5d3SJohn Marino {
408*86d7f5d3SJohn Marino #ifdef INET6
409*86d7f5d3SJohn Marino     return ftpPutURLAf(url, AF_UNSPEC, user, passwd, retcode);
410*86d7f5d3SJohn Marino #else
411*86d7f5d3SJohn Marino     return ftpPutURLAf(url, AF_INET, user, passwd, retcode);
412*86d7f5d3SJohn Marino #endif
413*86d7f5d3SJohn Marino 
414*86d7f5d3SJohn Marino }
415*86d7f5d3SJohn Marino 
416*86d7f5d3SJohn Marino FILE *
ftpPutURLAf(const char * url,int af,const char * user,const char * passwd,int * retcode)417*86d7f5d3SJohn Marino ftpPutURLAf(const char *url, int af, const char *user, const char *passwd,
418*86d7f5d3SJohn Marino 	    int *retcode)
419*86d7f5d3SJohn Marino {
420*86d7f5d3SJohn Marino     char host[255], name[255];
421*86d7f5d3SJohn Marino     int port;
422*86d7f5d3SJohn Marino     static FILE *fp = NULL;
423*86d7f5d3SJohn Marino     FILE *fp2;
424*86d7f5d3SJohn Marino 
425*86d7f5d3SJohn Marino     if (retcode)
426*86d7f5d3SJohn Marino 	*retcode = 0;
427*86d7f5d3SJohn Marino     if (fp) {	/* Close previous managed connection */
428*86d7f5d3SJohn Marino 	fclose(fp);
429*86d7f5d3SJohn Marino 	fp = NULL;
430*86d7f5d3SJohn Marino     }
431*86d7f5d3SJohn Marino     if (get_url_info(url, host, sizeof(host), &port,
432*86d7f5d3SJohn Marino 		     name, sizeof(name)) == SUCCESS) {
433*86d7f5d3SJohn Marino 	fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode);
434*86d7f5d3SJohn Marino 	if (fp) {
435*86d7f5d3SJohn Marino 	    fp2 = ftpPut(fp, name);
436*86d7f5d3SJohn Marino 	    if (!fp2) {
437*86d7f5d3SJohn Marino 		if (retcode)
438*86d7f5d3SJohn Marino 		    *retcode = ftpErrno(fp);
439*86d7f5d3SJohn Marino 		fclose(fp);
440*86d7f5d3SJohn Marino 		fp = NULL;
441*86d7f5d3SJohn Marino 	    }
442*86d7f5d3SJohn Marino 	    return fp2;
443*86d7f5d3SJohn Marino 	}
444*86d7f5d3SJohn Marino     }
445*86d7f5d3SJohn Marino     return NULL;
446*86d7f5d3SJohn Marino }
447*86d7f5d3SJohn Marino 
448*86d7f5d3SJohn Marino /* Internal workhorse function for dissecting URLs.  Takes a URL as the first argument and returns the
449*86d7f5d3SJohn Marino    result of such disection in the host, user, passwd, port and name variables. */
450*86d7f5d3SJohn Marino static int
get_url_info(const char * url_in,char * host_ret,size_t host_len,int * port_ret,char * name_ret,size_t name_len)451*86d7f5d3SJohn Marino get_url_info(const char *url_in, char *host_ret, size_t host_len,
452*86d7f5d3SJohn Marino 	     int *port_ret, char *name_ret, size_t name_len)
453*86d7f5d3SJohn Marino {
454*86d7f5d3SJohn Marino     char *name, *host, *cp, url[BUFSIZ];
455*86d7f5d3SJohn Marino     int port;
456*86d7f5d3SJohn Marino 
457*86d7f5d3SJohn Marino     name = host = NULL;
458*86d7f5d3SJohn Marino     /* XXX add http:// here or somewhere reasonable at some point XXX */
459*86d7f5d3SJohn Marino     if (strncmp("ftp://", url_in, 6) != 0)
460*86d7f5d3SJohn Marino 	return FAILURE;
461*86d7f5d3SJohn Marino     /* We like to stomp a lot on the URL string in dissecting it, so copy it first */
462*86d7f5d3SJohn Marino     strncpy(url, url_in, BUFSIZ);
463*86d7f5d3SJohn Marino     host = url + 6;
464*86d7f5d3SJohn Marino     if ((cp = index(host, ':')) != NULL) {
465*86d7f5d3SJohn Marino 	*(cp++) = '\0';
466*86d7f5d3SJohn Marino 	port = strtol(cp, 0, 0);
467*86d7f5d3SJohn Marino     }
468*86d7f5d3SJohn Marino     else
469*86d7f5d3SJohn Marino 	port = 0;	/* use default */
470*86d7f5d3SJohn Marino     if (port_ret)
471*86d7f5d3SJohn Marino 	*port_ret = port;
472*86d7f5d3SJohn Marino 
473*86d7f5d3SJohn Marino     if ((name = index(cp ? cp : host, '/')) != NULL)
474*86d7f5d3SJohn Marino 	*(name++) = '\0';
475*86d7f5d3SJohn Marino     if (host_ret) {
476*86d7f5d3SJohn Marino 	if (strlen(host) >= host_len)
477*86d7f5d3SJohn Marino 	    return FAILURE;
478*86d7f5d3SJohn Marino 	strcpy(host_ret, host);
479*86d7f5d3SJohn Marino     }
480*86d7f5d3SJohn Marino     if (name && name_ret) {
481*86d7f5d3SJohn Marino 	if (strlen(name) >= name_len)
482*86d7f5d3SJohn Marino 	    return FAILURE;
483*86d7f5d3SJohn Marino 	strcpy(name_ret, name);
484*86d7f5d3SJohn Marino     }
485*86d7f5d3SJohn Marino     return SUCCESS;
486*86d7f5d3SJohn Marino }
487*86d7f5d3SJohn Marino 
488*86d7f5d3SJohn Marino static FTP_t
ftp_new(void)489*86d7f5d3SJohn Marino ftp_new(void)
490*86d7f5d3SJohn Marino {
491*86d7f5d3SJohn Marino     FTP_t ftp;
492*86d7f5d3SJohn Marino 
493*86d7f5d3SJohn Marino     ftp = (FTP_t)malloc(sizeof *ftp);
494*86d7f5d3SJohn Marino     if (!ftp)
495*86d7f5d3SJohn Marino 	return NULL;
496*86d7f5d3SJohn Marino     memset(ftp, 0, sizeof *ftp);
497*86d7f5d3SJohn Marino     ftp->fd_ctrl = -1;
498*86d7f5d3SJohn Marino     ftp->con_state = init;
499*86d7f5d3SJohn Marino     ftp->is_binary = FALSE;
500*86d7f5d3SJohn Marino     ftp->is_passive = FALSE;
501*86d7f5d3SJohn Marino     ftp->is_verbose = FALSE;
502*86d7f5d3SJohn Marino     ftp->error = 0;
503*86d7f5d3SJohn Marino     return ftp;
504*86d7f5d3SJohn Marino }
505*86d7f5d3SJohn Marino 
506*86d7f5d3SJohn Marino static int
ftp_read_method(void * vp,char * buf,int nbytes)507*86d7f5d3SJohn Marino ftp_read_method(void *vp, char *buf, int nbytes)
508*86d7f5d3SJohn Marino {
509*86d7f5d3SJohn Marino     int i, fd;
510*86d7f5d3SJohn Marino     FTP_t n = (FTP_t)vp;
511*86d7f5d3SJohn Marino 
512*86d7f5d3SJohn Marino     fd = n->fd_ctrl;
513*86d7f5d3SJohn Marino     i = (fd >= 0) ? read(fd, buf, nbytes) : EOF;
514*86d7f5d3SJohn Marino     return i;
515*86d7f5d3SJohn Marino }
516*86d7f5d3SJohn Marino 
517*86d7f5d3SJohn Marino static int
ftp_write_method(void * vp,const char * buf,int nbytes)518*86d7f5d3SJohn Marino ftp_write_method(void *vp, const char *buf, int nbytes)
519*86d7f5d3SJohn Marino {
520*86d7f5d3SJohn Marino     int i, fd;
521*86d7f5d3SJohn Marino     FTP_t n = (FTP_t)vp;
522*86d7f5d3SJohn Marino 
523*86d7f5d3SJohn Marino     fd = n->fd_ctrl;
524*86d7f5d3SJohn Marino     i = (fd >= 0) ? write(fd, buf, nbytes) : EOF;
525*86d7f5d3SJohn Marino     return i;
526*86d7f5d3SJohn Marino }
527*86d7f5d3SJohn Marino 
528*86d7f5d3SJohn Marino static int
ftp_close_method(void * n)529*86d7f5d3SJohn Marino ftp_close_method(void *n)
530*86d7f5d3SJohn Marino {
531*86d7f5d3SJohn Marino     int i;
532*86d7f5d3SJohn Marino 
533*86d7f5d3SJohn Marino     i = ftp_close((FTP_t)n);
534*86d7f5d3SJohn Marino     free(n);
535*86d7f5d3SJohn Marino     return i;
536*86d7f5d3SJohn Marino }
537*86d7f5d3SJohn Marino 
538*86d7f5d3SJohn Marino /*
539*86d7f5d3SJohn Marino  * This function checks whether the FTP_PASSIVE_MODE environment
540*86d7f5d3SJohn Marino  * variable is set, and, if so, enforces the desired mode.
541*86d7f5d3SJohn Marino  */
542*86d7f5d3SJohn Marino static void
check_passive(FILE * fp)543*86d7f5d3SJohn Marino check_passive(FILE *fp)
544*86d7f5d3SJohn Marino {
545*86d7f5d3SJohn Marino     const char *cp = getenv("FTP_PASSIVE_MODE");
546*86d7f5d3SJohn Marino 
547*86d7f5d3SJohn Marino     if (cp != NULL)
548*86d7f5d3SJohn Marino     	ftpPassive(fp, strncasecmp(cp, "no", 2));
549*86d7f5d3SJohn Marino }
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino static void
ftp_timeout(int sig __unused)552*86d7f5d3SJohn Marino ftp_timeout(int sig __unused)
553*86d7f5d3SJohn Marino {
554*86d7f5d3SJohn Marino     FtpTimedOut = TRUE;
555*86d7f5d3SJohn Marino     /* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */
556*86d7f5d3SJohn Marino }
557*86d7f5d3SJohn Marino 
558*86d7f5d3SJohn Marino static void
ftp_set_timeout(void)559*86d7f5d3SJohn Marino ftp_set_timeout(void)
560*86d7f5d3SJohn Marino {
561*86d7f5d3SJohn Marino     struct sigaction new;
562*86d7f5d3SJohn Marino     char *cp;
563*86d7f5d3SJohn Marino     int ival;
564*86d7f5d3SJohn Marino 
565*86d7f5d3SJohn Marino     FtpTimedOut = FALSE;
566*86d7f5d3SJohn Marino     sigemptyset(&new.sa_mask);
567*86d7f5d3SJohn Marino     new.sa_flags = 0;
568*86d7f5d3SJohn Marino     new.sa_handler = ftp_timeout;
569*86d7f5d3SJohn Marino     sigaction(SIGALRM, &new, NULL);
570*86d7f5d3SJohn Marino     cp = getenv("FTP_TIMEOUT");
571*86d7f5d3SJohn Marino     if (!cp || !(ival = atoi(cp)))
572*86d7f5d3SJohn Marino 	ival = 120;
573*86d7f5d3SJohn Marino     alarm(ival);
574*86d7f5d3SJohn Marino }
575*86d7f5d3SJohn Marino 
576*86d7f5d3SJohn Marino static void
ftp_clear_timeout(void)577*86d7f5d3SJohn Marino ftp_clear_timeout(void)
578*86d7f5d3SJohn Marino {
579*86d7f5d3SJohn Marino     struct sigaction new;
580*86d7f5d3SJohn Marino 
581*86d7f5d3SJohn Marino     alarm(0);
582*86d7f5d3SJohn Marino     sigemptyset(&new.sa_mask);
583*86d7f5d3SJohn Marino     new.sa_flags = 0;
584*86d7f5d3SJohn Marino     new.sa_handler = SIG_DFL;
585*86d7f5d3SJohn Marino     sigaction(SIGALRM, &new, NULL);
586*86d7f5d3SJohn Marino }
587*86d7f5d3SJohn Marino 
588*86d7f5d3SJohn Marino static int
writes(int fd,const char * s)589*86d7f5d3SJohn Marino writes(int fd, const char *s)
590*86d7f5d3SJohn Marino {
591*86d7f5d3SJohn Marino     int n, i = strlen(s);
592*86d7f5d3SJohn Marino 
593*86d7f5d3SJohn Marino     ftp_set_timeout();
594*86d7f5d3SJohn Marino     n = write(fd, s, i);
595*86d7f5d3SJohn Marino     ftp_clear_timeout();
596*86d7f5d3SJohn Marino     if (FtpTimedOut || i != n)
597*86d7f5d3SJohn Marino 	return TRUE;
598*86d7f5d3SJohn Marino     return FALSE;
599*86d7f5d3SJohn Marino }
600*86d7f5d3SJohn Marino 
601*86d7f5d3SJohn Marino static char *
get_a_line(FTP_t ftp)602*86d7f5d3SJohn Marino get_a_line(FTP_t ftp)
603*86d7f5d3SJohn Marino {
604*86d7f5d3SJohn Marino     static char buf[BUFSIZ];
605*86d7f5d3SJohn Marino     int i,j;
606*86d7f5d3SJohn Marino 
607*86d7f5d3SJohn Marino     /* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */
608*86d7f5d3SJohn Marino     for(i = 0; i < BUFSIZ;) {
609*86d7f5d3SJohn Marino 	ftp_set_timeout();
610*86d7f5d3SJohn Marino 	j = read(ftp->fd_ctrl, buf + i, 1);
611*86d7f5d3SJohn Marino 	ftp_clear_timeout();
612*86d7f5d3SJohn Marino 	if (FtpTimedOut || j != 1)
613*86d7f5d3SJohn Marino 	    return NULL;
614*86d7f5d3SJohn Marino 	if (buf[i] == '\r' || buf[i] == '\n') {
615*86d7f5d3SJohn Marino 	    if (!i)
616*86d7f5d3SJohn Marino 		continue;
617*86d7f5d3SJohn Marino 	    buf[i] = '\0';
618*86d7f5d3SJohn Marino 	    if (ftp->is_verbose == TRUE)
619*86d7f5d3SJohn Marino 		fprintf(stderr, "%s\n",buf+4);
620*86d7f5d3SJohn Marino 	    return buf;
621*86d7f5d3SJohn Marino 	}
622*86d7f5d3SJohn Marino 	i++;
623*86d7f5d3SJohn Marino     }
624*86d7f5d3SJohn Marino     /* Debug("ftp_pkg: read string \"%s\" from %d", buf, ftp->fd_ctrl); */
625*86d7f5d3SJohn Marino     return buf;
626*86d7f5d3SJohn Marino }
627*86d7f5d3SJohn Marino 
628*86d7f5d3SJohn Marino static int
get_a_number(FTP_t ftp,char ** q)629*86d7f5d3SJohn Marino get_a_number(FTP_t ftp, char **q)
630*86d7f5d3SJohn Marino {
631*86d7f5d3SJohn Marino     char *p;
632*86d7f5d3SJohn Marino     int i = -1, j;
633*86d7f5d3SJohn Marino 
634*86d7f5d3SJohn Marino     while(1) {
635*86d7f5d3SJohn Marino 	p = get_a_line(ftp);
636*86d7f5d3SJohn Marino 	if (!p) {
637*86d7f5d3SJohn Marino 	    ftp_close(ftp);
638*86d7f5d3SJohn Marino 	    if (FtpTimedOut)
639*86d7f5d3SJohn Marino 		return FTP_TIMED_OUT;
640*86d7f5d3SJohn Marino 	    return FAILURE;
641*86d7f5d3SJohn Marino 	}
642*86d7f5d3SJohn Marino 	if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])))
643*86d7f5d3SJohn Marino 	    continue;
644*86d7f5d3SJohn Marino 	if (i == -1 && p[3] == '-') {
645*86d7f5d3SJohn Marino 	    i = strtol(p, 0, 0);
646*86d7f5d3SJohn Marino 	    continue;
647*86d7f5d3SJohn Marino 	}
648*86d7f5d3SJohn Marino 	if (p[3] != ' ' && p[3] != '\t')
649*86d7f5d3SJohn Marino 	    continue;
650*86d7f5d3SJohn Marino 	j = strtol(p, 0, 0);
651*86d7f5d3SJohn Marino 	if (i == -1) {
652*86d7f5d3SJohn Marino 	    if (q) *q = p+4;
653*86d7f5d3SJohn Marino 	    /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
654*86d7f5d3SJohn Marino 	    return j;
655*86d7f5d3SJohn Marino 	} else if (j == i) {
656*86d7f5d3SJohn Marino 	    if (q) *q = p+4;
657*86d7f5d3SJohn Marino 	    /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
658*86d7f5d3SJohn Marino 	    return j;
659*86d7f5d3SJohn Marino 	}
660*86d7f5d3SJohn Marino     }
661*86d7f5d3SJohn Marino }
662*86d7f5d3SJohn Marino 
663*86d7f5d3SJohn Marino static int
ftp_close(FTP_t ftp)664*86d7f5d3SJohn Marino ftp_close(FTP_t ftp)
665*86d7f5d3SJohn Marino {
666*86d7f5d3SJohn Marino     int i, rcode;
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino     rcode = FAILURE;
669*86d7f5d3SJohn Marino     if (ftp->con_state == isopen) {
670*86d7f5d3SJohn Marino 	ftp->con_state = quit;
671*86d7f5d3SJohn Marino 	/* If last operation timed out, don't try to quit - just close */
672*86d7f5d3SJohn Marino 	if (ftp->error != FTP_TIMED_OUT)
673*86d7f5d3SJohn Marino 	    i = cmd(ftp, "QUIT");
674*86d7f5d3SJohn Marino 	else
675*86d7f5d3SJohn Marino 	    i = FTP_QUIT_HAPPY;
676*86d7f5d3SJohn Marino 	if (!check_code(ftp, i, FTP_QUIT_HAPPY))
677*86d7f5d3SJohn Marino 	    rcode = SUCCESS;
678*86d7f5d3SJohn Marino 	close(ftp->fd_ctrl);
679*86d7f5d3SJohn Marino 	ftp->fd_ctrl = -1;
680*86d7f5d3SJohn Marino     }
681*86d7f5d3SJohn Marino     else if (ftp->con_state == quit)
682*86d7f5d3SJohn Marino 	rcode = SUCCESS;
683*86d7f5d3SJohn Marino     return rcode;
684*86d7f5d3SJohn Marino }
685*86d7f5d3SJohn Marino 
686*86d7f5d3SJohn Marino static int
botch(const char * func __unused,const char * botch_state __unused)687*86d7f5d3SJohn Marino botch(const char *func __unused, const char *botch_state __unused)
688*86d7f5d3SJohn Marino {
689*86d7f5d3SJohn Marino     /* Debug("ftp_pkg: botch: %s(%s)", func, botch_state); */
690*86d7f5d3SJohn Marino     return FAILURE;
691*86d7f5d3SJohn Marino }
692*86d7f5d3SJohn Marino 
693*86d7f5d3SJohn Marino static int
cmd(FTP_t ftp,const char * fmt,...)694*86d7f5d3SJohn Marino cmd(FTP_t ftp, const char *fmt, ...)
695*86d7f5d3SJohn Marino {
696*86d7f5d3SJohn Marino     char p[BUFSIZ];
697*86d7f5d3SJohn Marino     int i;
698*86d7f5d3SJohn Marino 
699*86d7f5d3SJohn Marino     va_list ap;
700*86d7f5d3SJohn Marino     va_start(ap, fmt);
701*86d7f5d3SJohn Marino     if ((size_t)vsnprintf(p, sizeof p - 2, fmt, ap) >= sizeof(p) - 2)
702*86d7f5d3SJohn Marino 	return FAILURE;
703*86d7f5d3SJohn Marino     va_end(ap);
704*86d7f5d3SJohn Marino 
705*86d7f5d3SJohn Marino     if (ftp->con_state == init)
706*86d7f5d3SJohn Marino 	return botch("cmd", "open");
707*86d7f5d3SJohn Marino 
708*86d7f5d3SJohn Marino     strcat(p, "\r\n");
709*86d7f5d3SJohn Marino     if (ftp->is_verbose)
710*86d7f5d3SJohn Marino 	fprintf(stderr, "Sending: %s", p);
711*86d7f5d3SJohn Marino     if (writes(ftp->fd_ctrl, p)) {
712*86d7f5d3SJohn Marino 	if (FtpTimedOut)
713*86d7f5d3SJohn Marino 	    return FTP_TIMED_OUT;
714*86d7f5d3SJohn Marino 	return FAILURE;
715*86d7f5d3SJohn Marino     }
716*86d7f5d3SJohn Marino     while ((i = get_a_number(ftp, NULL)) == FTP_HAPPY_COMMENT);
717*86d7f5d3SJohn Marino     return i;
718*86d7f5d3SJohn Marino }
719*86d7f5d3SJohn Marino 
720*86d7f5d3SJohn Marino static int
ftp_login_session(FTP_t ftp,const char * host,int af,const char * user,const char * passwd,int port,int verbose)721*86d7f5d3SJohn Marino ftp_login_session(FTP_t ftp, const char *host, int af,
722*86d7f5d3SJohn Marino 		  const char *user, const char *passwd, int port, int verbose)
723*86d7f5d3SJohn Marino {
724*86d7f5d3SJohn Marino     char pbuf[10];
725*86d7f5d3SJohn Marino     struct addrinfo	hints, *res, *res0;
726*86d7f5d3SJohn Marino     int			err;
727*86d7f5d3SJohn Marino     int 		s;
728*86d7f5d3SJohn Marino     int			i;
729*86d7f5d3SJohn Marino 
730*86d7f5d3SJohn Marino     if (networkInit() != SUCCESS)
731*86d7f5d3SJohn Marino 	return FAILURE;
732*86d7f5d3SJohn Marino 
733*86d7f5d3SJohn Marino     if (ftp->con_state != init) {
734*86d7f5d3SJohn Marino 	ftp_close(ftp);
735*86d7f5d3SJohn Marino 	ftp->error = -1;
736*86d7f5d3SJohn Marino 	return FAILURE;
737*86d7f5d3SJohn Marino     }
738*86d7f5d3SJohn Marino 
739*86d7f5d3SJohn Marino     if (!user)
740*86d7f5d3SJohn Marino 	user = "ftp";
741*86d7f5d3SJohn Marino 
742*86d7f5d3SJohn Marino     if (!passwd)
743*86d7f5d3SJohn Marino 	passwd = "setup@";
744*86d7f5d3SJohn Marino 
745*86d7f5d3SJohn Marino     if (!port)
746*86d7f5d3SJohn Marino 	port = 21;
747*86d7f5d3SJohn Marino 
748*86d7f5d3SJohn Marino     if ((size_t)snprintf(pbuf, sizeof(pbuf), "%d", port) >= sizeof(pbuf))
749*86d7f5d3SJohn Marino 	return FAILURE;
750*86d7f5d3SJohn Marino     memset(&hints, 0, sizeof(hints));
751*86d7f5d3SJohn Marino     hints.ai_family = af;
752*86d7f5d3SJohn Marino     hints.ai_socktype = SOCK_STREAM;
753*86d7f5d3SJohn Marino     hints.ai_protocol = 0;
754*86d7f5d3SJohn Marino     err = getaddrinfo(host, pbuf, &hints, &res0);
755*86d7f5d3SJohn Marino     if (err) {
756*86d7f5d3SJohn Marino 	ftp->error = 0;
757*86d7f5d3SJohn Marino 	return FAILURE;
758*86d7f5d3SJohn Marino     }
759*86d7f5d3SJohn Marino 
760*86d7f5d3SJohn Marino     s = -1;
761*86d7f5d3SJohn Marino     for (res = res0; res; res = res->ai_next) {
762*86d7f5d3SJohn Marino 	ai_unmapped(res);
763*86d7f5d3SJohn Marino 	ftp->addrtype = res->ai_family;
764*86d7f5d3SJohn Marino 
765*86d7f5d3SJohn Marino 	if ((s = socket(res->ai_family, res->ai_socktype,
766*86d7f5d3SJohn Marino 			res->ai_protocol)) < 0)
767*86d7f5d3SJohn Marino 	    continue;
768*86d7f5d3SJohn Marino 
769*86d7f5d3SJohn Marino 	if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
770*86d7f5d3SJohn Marino 	    close(s);
771*86d7f5d3SJohn Marino 	    s = -1;
772*86d7f5d3SJohn Marino 	    continue;
773*86d7f5d3SJohn Marino 	}
774*86d7f5d3SJohn Marino 
775*86d7f5d3SJohn Marino 	break;
776*86d7f5d3SJohn Marino     }
777*86d7f5d3SJohn Marino     freeaddrinfo(res0);
778*86d7f5d3SJohn Marino     if (s < 0) {
779*86d7f5d3SJohn Marino 	ftp->error = errno;
780*86d7f5d3SJohn Marino 	return FAILURE;
781*86d7f5d3SJohn Marino     }
782*86d7f5d3SJohn Marino 
783*86d7f5d3SJohn Marino     ftp->fd_ctrl = s;
784*86d7f5d3SJohn Marino     ftp->con_state = isopen;
785*86d7f5d3SJohn Marino     ftp->is_verbose = verbose;
786*86d7f5d3SJohn Marino 
787*86d7f5d3SJohn Marino     i = cmd(ftp, "USER %s", user);
788*86d7f5d3SJohn Marino     if (i >= 300 && i < 400)
789*86d7f5d3SJohn Marino 	i = cmd(ftp, "PASS %s", passwd);
790*86d7f5d3SJohn Marino     if (i >= 299 || i < 0) {
791*86d7f5d3SJohn Marino 	ftp_close(ftp);
792*86d7f5d3SJohn Marino 	if (i > 0)
793*86d7f5d3SJohn Marino 	    ftp->error = i;
794*86d7f5d3SJohn Marino 	return FAILURE;
795*86d7f5d3SJohn Marino     }
796*86d7f5d3SJohn Marino     return SUCCESS;
797*86d7f5d3SJohn Marino }
798*86d7f5d3SJohn Marino 
799*86d7f5d3SJohn Marino static int
ftp_file_op(FTP_t ftp,const char * operation,const char * file,FILE ** fp,const char * mode,off_t * seekto)800*86d7f5d3SJohn Marino ftp_file_op(FTP_t ftp, const char *operation, const char *file, FILE **fp,
801*86d7f5d3SJohn Marino 	    const char *mode, off_t *seekto)
802*86d7f5d3SJohn Marino {
803*86d7f5d3SJohn Marino     int i,l,s;
804*86d7f5d3SJohn Marino     char *q;
805*86d7f5d3SJohn Marino     unsigned char addr[64];
806*86d7f5d3SJohn Marino     union sockaddr_cmn {
807*86d7f5d3SJohn Marino 	struct sockaddr_in sin4;
808*86d7f5d3SJohn Marino 	struct sockaddr_in6 sin6;
809*86d7f5d3SJohn Marino     } sin;
810*86d7f5d3SJohn Marino     const char *cmdstr;
811*86d7f5d3SJohn Marino 
812*86d7f5d3SJohn Marino     if (!fp)
813*86d7f5d3SJohn Marino 	return FAILURE;
814*86d7f5d3SJohn Marino     *fp = NULL;
815*86d7f5d3SJohn Marino 
816*86d7f5d3SJohn Marino     if (ftp->con_state != isopen)
817*86d7f5d3SJohn Marino 	return botch("ftp_file_op", "open");
818*86d7f5d3SJohn Marino 
819*86d7f5d3SJohn Marino     if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
820*86d7f5d3SJohn Marino 	ftp->error = errno;
821*86d7f5d3SJohn Marino 	return FAILURE;
822*86d7f5d3SJohn Marino     }
823*86d7f5d3SJohn Marino 
824*86d7f5d3SJohn Marino     if (ftp->is_passive) {
825*86d7f5d3SJohn Marino 	if (ftp->addrtype == AF_INET) {
826*86d7f5d3SJohn Marino             if (ftp->is_verbose)
827*86d7f5d3SJohn Marino 		fprintf(stderr, "Sending PASV\n");
828*86d7f5d3SJohn Marino 	    if (writes(ftp->fd_ctrl, "PASV\r\n")) {
829*86d7f5d3SJohn Marino 		ftp_close(ftp);
830*86d7f5d3SJohn Marino 		if (FtpTimedOut)
831*86d7f5d3SJohn Marino 		    ftp->error = FTP_TIMED_OUT;
832*86d7f5d3SJohn Marino 		return FTP_TIMED_OUT;
833*86d7f5d3SJohn Marino 	    }
834*86d7f5d3SJohn Marino 	    i = get_a_number(ftp, &q);
835*86d7f5d3SJohn Marino 	    if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) {
836*86d7f5d3SJohn Marino 		ftp_close(ftp);
837*86d7f5d3SJohn Marino 		return i;
838*86d7f5d3SJohn Marino 	    }
839*86d7f5d3SJohn Marino 	    cmdstr = "PASV";
840*86d7f5d3SJohn Marino 	} else {
841*86d7f5d3SJohn Marino             if (ftp->is_verbose)
842*86d7f5d3SJohn Marino 		fprintf(stderr, "Sending EPSV\n");
843*86d7f5d3SJohn Marino 	    if (writes(ftp->fd_ctrl, "EPSV\r\n")) {
844*86d7f5d3SJohn Marino 		ftp_close(ftp);
845*86d7f5d3SJohn Marino 		if (FtpTimedOut)
846*86d7f5d3SJohn Marino 		    ftp->error = FTP_TIMED_OUT;
847*86d7f5d3SJohn Marino 		return FTP_TIMED_OUT;
848*86d7f5d3SJohn Marino 	    }
849*86d7f5d3SJohn Marino 	    i = get_a_number(ftp, &q);
850*86d7f5d3SJohn Marino 	    if (check_code(ftp, i, FTP_EPASSIVE_HAPPY)) {
851*86d7f5d3SJohn Marino 		if (ftp->is_verbose)
852*86d7f5d3SJohn Marino 		    fprintf(stderr, "Sending LPSV\n");
853*86d7f5d3SJohn Marino 		if (writes(ftp->fd_ctrl, "LPSV\r\n")) {
854*86d7f5d3SJohn Marino 		    ftp_close(ftp);
855*86d7f5d3SJohn Marino 		    if (FtpTimedOut)
856*86d7f5d3SJohn Marino 			ftp->error = FTP_TIMED_OUT;
857*86d7f5d3SJohn Marino 		    return FTP_TIMED_OUT;
858*86d7f5d3SJohn Marino 		}
859*86d7f5d3SJohn Marino 		i = get_a_number(ftp, &q);
860*86d7f5d3SJohn Marino 		if (check_code(ftp, i, FTP_LPASSIVE_HAPPY)) {
861*86d7f5d3SJohn Marino 		    ftp_close(ftp);
862*86d7f5d3SJohn Marino 		    return i;
863*86d7f5d3SJohn Marino 		}
864*86d7f5d3SJohn Marino 		cmdstr = "LPSV";
865*86d7f5d3SJohn Marino 	    } else
866*86d7f5d3SJohn Marino 		cmdstr = "EPSV";
867*86d7f5d3SJohn Marino 	}
868*86d7f5d3SJohn Marino 	if (strcmp(cmdstr, "PASV") == 0 || strcmp(cmdstr, "LPSV") == 0) {
869*86d7f5d3SJohn Marino 	    while (*q && !isdigit(*q))
870*86d7f5d3SJohn Marino 		q++;
871*86d7f5d3SJohn Marino 	    if (!*q) {
872*86d7f5d3SJohn Marino 		ftp_close(ftp);
873*86d7f5d3SJohn Marino 		return FAILURE;
874*86d7f5d3SJohn Marino 	    }
875*86d7f5d3SJohn Marino 	    q--;
876*86d7f5d3SJohn Marino 	    l = (ftp->addrtype == AF_INET ? 6 : 21);
877*86d7f5d3SJohn Marino 	    for (i = 0; i < l; i++) {
878*86d7f5d3SJohn Marino 		q++;
879*86d7f5d3SJohn Marino 		addr[i] = strtol(q, &q, 10);
880*86d7f5d3SJohn Marino 	    }
881*86d7f5d3SJohn Marino 
882*86d7f5d3SJohn Marino 	    sin.sin4.sin_family = ftp->addrtype;
883*86d7f5d3SJohn Marino 	    if (ftp->addrtype == AF_INET6) {
884*86d7f5d3SJohn Marino 		sin.sin6.sin6_len = sizeof(struct sockaddr_in6);
885*86d7f5d3SJohn Marino 		bcopy(addr + 2, (char *)&sin.sin6.sin6_addr, 16);
886*86d7f5d3SJohn Marino 		bcopy(addr + 19, (char *)&sin.sin6.sin6_port, 2);
887*86d7f5d3SJohn Marino 	    } else {
888*86d7f5d3SJohn Marino 		sin.sin4.sin_len = sizeof(struct sockaddr_in);
889*86d7f5d3SJohn Marino 		bcopy(addr, (char *)&sin.sin4.sin_addr, 4);
890*86d7f5d3SJohn Marino 		bcopy(addr + 4, (char *)&sin.sin4.sin_port, 2);
891*86d7f5d3SJohn Marino 	    }
892*86d7f5d3SJohn Marino 	} else if (strcmp(cmdstr, "EPSV") == 0) {
893*86d7f5d3SJohn Marino 	    int port;
894*86d7f5d3SJohn Marino 	    int sinlen;
895*86d7f5d3SJohn Marino 	    while (*q && *q != '(')		/* ) */
896*86d7f5d3SJohn Marino 		q++;
897*86d7f5d3SJohn Marino 	    if (!*q) {
898*86d7f5d3SJohn Marino 		ftp_close(ftp);
899*86d7f5d3SJohn Marino 		return FAILURE;
900*86d7f5d3SJohn Marino 	    }
901*86d7f5d3SJohn Marino 	    q++;
902*86d7f5d3SJohn Marino 	    if (sscanf(q, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
903*86d7f5d3SJohn Marino 		    &port, &addr[3]) != 5
904*86d7f5d3SJohn Marino 	     || addr[0] != addr[1] || addr[0] != addr[2] || addr[0] != addr[3]) {
905*86d7f5d3SJohn Marino 		ftp_close(ftp);
906*86d7f5d3SJohn Marino 		return FAILURE;
907*86d7f5d3SJohn Marino 	    }
908*86d7f5d3SJohn Marino 	    sinlen = sizeof(sin);
909*86d7f5d3SJohn Marino 	    if (getpeername(ftp->fd_ctrl, (struct sockaddr *)&sin, &sinlen) < 0) {
910*86d7f5d3SJohn Marino 		ftp_close(ftp);
911*86d7f5d3SJohn Marino 		return FAILURE;
912*86d7f5d3SJohn Marino 	    }
913*86d7f5d3SJohn Marino 	    switch (sin.sin4.sin_family) {
914*86d7f5d3SJohn Marino 	    case AF_INET:
915*86d7f5d3SJohn Marino 		sin.sin4.sin_port = htons(port);
916*86d7f5d3SJohn Marino 		break;
917*86d7f5d3SJohn Marino 	    case AF_INET6:
918*86d7f5d3SJohn Marino 		sin.sin6.sin6_port = htons(port);
919*86d7f5d3SJohn Marino 		break;
920*86d7f5d3SJohn Marino 	    default:
921*86d7f5d3SJohn Marino 		ftp_close(ftp);
922*86d7f5d3SJohn Marino 		return FAILURE;
923*86d7f5d3SJohn Marino 	    }
924*86d7f5d3SJohn Marino 	}
925*86d7f5d3SJohn Marino 
926*86d7f5d3SJohn Marino 	if (connect(s, (struct sockaddr *)&sin, sin.sin4.sin_len) < 0) {
927*86d7f5d3SJohn Marino 	    (void)close(s);
928*86d7f5d3SJohn Marino 	    return FAILURE;
929*86d7f5d3SJohn Marino 	}
930*86d7f5d3SJohn Marino 
931*86d7f5d3SJohn Marino 	if (seekto && *seekto) {
932*86d7f5d3SJohn Marino 	    i = cmd(ftp, "REST %" PRId64, *seekto);
933*86d7f5d3SJohn Marino 	    if (i < 0 || FTP_TIMEOUT(i)) {
934*86d7f5d3SJohn Marino 		close(s);
935*86d7f5d3SJohn Marino 		ftp->error = i;
936*86d7f5d3SJohn Marino 		*seekto = (off_t)0;
937*86d7f5d3SJohn Marino 		return i;
938*86d7f5d3SJohn Marino 	    }
939*86d7f5d3SJohn Marino 	}
940*86d7f5d3SJohn Marino 	i = cmd(ftp, "%s %s", operation, file);
941*86d7f5d3SJohn Marino 	if (i < 0 || i > 299) {
942*86d7f5d3SJohn Marino 	    close(s);
943*86d7f5d3SJohn Marino 	    ftp->error = i;
944*86d7f5d3SJohn Marino 	    return i;
945*86d7f5d3SJohn Marino 	}
946*86d7f5d3SJohn Marino 	*fp = fdopen(s, mode);
947*86d7f5d3SJohn Marino     }
948*86d7f5d3SJohn Marino     else {
949*86d7f5d3SJohn Marino 	int fd,portrange;
950*86d7f5d3SJohn Marino 
951*86d7f5d3SJohn Marino #ifdef IPV6_PORTRANGE
952*86d7f5d3SJohn Marino 	if (ftp->addrtype == AF_INET6) {
953*86d7f5d3SJohn Marino 		portrange = IPV6_PORTRANGE_HIGH;
954*86d7f5d3SJohn Marino 		if (setsockopt(s, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)
955*86d7f5d3SJohn Marino 			       &portrange, sizeof(portrange)) < 0) {
956*86d7f5d3SJohn Marino 			close(s);
957*86d7f5d3SJohn Marino 			return FAILURE;
958*86d7f5d3SJohn Marino 		}
959*86d7f5d3SJohn Marino 	}
960*86d7f5d3SJohn Marino #endif
961*86d7f5d3SJohn Marino #ifdef IP_PORTRANGE
962*86d7f5d3SJohn Marino 	if (ftp->addrtype == AF_INET) {
963*86d7f5d3SJohn Marino 		portrange = IP_PORTRANGE_HIGH;
964*86d7f5d3SJohn Marino 		if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *)
965*86d7f5d3SJohn Marino 			       &portrange, sizeof(portrange)) < 0) {
966*86d7f5d3SJohn Marino 			close(s);
967*86d7f5d3SJohn Marino 			return FAILURE;
968*86d7f5d3SJohn Marino 		}
969*86d7f5d3SJohn Marino 	}
970*86d7f5d3SJohn Marino #endif
971*86d7f5d3SJohn Marino 
972*86d7f5d3SJohn Marino 	i = sizeof sin;
973*86d7f5d3SJohn Marino 	getsockname(ftp->fd_ctrl, (struct sockaddr *)&sin, &i);
974*86d7f5d3SJohn Marino 	sin.sin4.sin_port = 0;
975*86d7f5d3SJohn Marino 	i = ((struct sockaddr *)&sin)->sa_len;
976*86d7f5d3SJohn Marino 	if (bind(s, (struct sockaddr *)&sin, i) < 0) {
977*86d7f5d3SJohn Marino 	    close(s);
978*86d7f5d3SJohn Marino 	    return FAILURE;
979*86d7f5d3SJohn Marino 	}
980*86d7f5d3SJohn Marino 	i = sizeof sin;
981*86d7f5d3SJohn Marino 	getsockname(s,(struct sockaddr *)&sin,&i);
982*86d7f5d3SJohn Marino 	if (listen(s, 1) < 0) {
983*86d7f5d3SJohn Marino 	    close(s);
984*86d7f5d3SJohn Marino 	    return FAILURE;
985*86d7f5d3SJohn Marino 	}
986*86d7f5d3SJohn Marino 	if (sin.sin4.sin_family == AF_INET) {
987*86d7f5d3SJohn Marino             u_long a;
988*86d7f5d3SJohn Marino 	    a = ntohl(sin.sin4.sin_addr.s_addr);
989*86d7f5d3SJohn Marino 	    i = cmd(ftp, "PORT %ld,%ld,%ld,%ld,%d,%d",
990*86d7f5d3SJohn Marino 		    (a                   >> 24) & 0xff,
991*86d7f5d3SJohn Marino 		    (a                   >> 16) & 0xff,
992*86d7f5d3SJohn Marino 		    (a                   >>  8) & 0xff,
993*86d7f5d3SJohn Marino 		    a                           & 0xff,
994*86d7f5d3SJohn Marino 		    (ntohs(sin.sin4.sin_port) >>  8) & 0xff,
995*86d7f5d3SJohn Marino 		    ntohs(sin.sin4.sin_port)         & 0xff);
996*86d7f5d3SJohn Marino 	    if (check_code(ftp, i, FTP_PORT_HAPPY)) {
997*86d7f5d3SJohn Marino 		close(s);
998*86d7f5d3SJohn Marino 		return i;
999*86d7f5d3SJohn Marino 	    }
1000*86d7f5d3SJohn Marino 	} else {
1001*86d7f5d3SJohn Marino #define UC(b)	(((int)b)&0xff)
1002*86d7f5d3SJohn Marino 	    char *a;
1003*86d7f5d3SJohn Marino 	    char hname[INET6_ADDRSTRLEN];
1004*86d7f5d3SJohn Marino 
1005*86d7f5d3SJohn Marino 	    sin.sin6.sin6_scope_id = 0;
1006*86d7f5d3SJohn Marino 	    if (getnameinfo((struct sockaddr *)&sin, sin.sin6.sin6_len,
1007*86d7f5d3SJohn Marino 			    hname, sizeof(hname),
1008*86d7f5d3SJohn Marino 			    NULL, 0, NI_NUMERICHOST) != 0) {
1009*86d7f5d3SJohn Marino 		goto try_lprt;
1010*86d7f5d3SJohn Marino 	    }
1011*86d7f5d3SJohn Marino 	    i = cmd(ftp, "EPRT |%d|%s|%d|", 2, hname,
1012*86d7f5d3SJohn Marino 		    htons(sin.sin6.sin6_port));
1013*86d7f5d3SJohn Marino 	    if (check_code(ftp, i, FTP_PORT_HAPPY)) {
1014*86d7f5d3SJohn Marino try_lprt:
1015*86d7f5d3SJohn Marino 		a = (char *)&sin.sin6.sin6_addr;
1016*86d7f5d3SJohn Marino 		i = cmd(ftp,
1017*86d7f5d3SJohn Marino "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1018*86d7f5d3SJohn Marino 			6, 16,
1019*86d7f5d3SJohn Marino 			UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]),
1020*86d7f5d3SJohn Marino 			UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]),
1021*86d7f5d3SJohn Marino 			UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]),
1022*86d7f5d3SJohn Marino 			UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]),
1023*86d7f5d3SJohn Marino 			2,
1024*86d7f5d3SJohn Marino 			(ntohs(sin.sin4.sin_port) >>  8) & 0xff,
1025*86d7f5d3SJohn Marino 			ntohs(sin.sin4.sin_port)         & 0xff);
1026*86d7f5d3SJohn Marino 		if (check_code(ftp, i, FTP_PORT_HAPPY)) {
1027*86d7f5d3SJohn Marino 		    close(s);
1028*86d7f5d3SJohn Marino 		    return i;
1029*86d7f5d3SJohn Marino 		}
1030*86d7f5d3SJohn Marino 	    }
1031*86d7f5d3SJohn Marino 	}
1032*86d7f5d3SJohn Marino 	if (seekto && *seekto) {
1033*86d7f5d3SJohn Marino 	    i = cmd(ftp, "REST %" PRId64, *seekto);
1034*86d7f5d3SJohn Marino 	    if (i < 0 || FTP_TIMEOUT(i)) {
1035*86d7f5d3SJohn Marino 		close(s);
1036*86d7f5d3SJohn Marino 		ftp->error = i;
1037*86d7f5d3SJohn Marino 		return i;
1038*86d7f5d3SJohn Marino 	    }
1039*86d7f5d3SJohn Marino 	    else if (i != 350)
1040*86d7f5d3SJohn Marino 		*seekto = (off_t)0;
1041*86d7f5d3SJohn Marino 	}
1042*86d7f5d3SJohn Marino 	i = cmd(ftp, "%s %s", operation, file);
1043*86d7f5d3SJohn Marino 	if (i < 0 || i > 299) {
1044*86d7f5d3SJohn Marino 	    close(s);
1045*86d7f5d3SJohn Marino 	    ftp->error = i;
1046*86d7f5d3SJohn Marino 	    return FAILURE;
1047*86d7f5d3SJohn Marino 	}
1048*86d7f5d3SJohn Marino 	fd = accept(s, 0, 0);
1049*86d7f5d3SJohn Marino 	if (fd < 0) {
1050*86d7f5d3SJohn Marino 	    close(s);
1051*86d7f5d3SJohn Marino 	    ftp->error = 401;
1052*86d7f5d3SJohn Marino 	    return FAILURE;
1053*86d7f5d3SJohn Marino 	}
1054*86d7f5d3SJohn Marino 	close(s);
1055*86d7f5d3SJohn Marino 	*fp = fdopen(fd, mode);
1056*86d7f5d3SJohn Marino     }
1057*86d7f5d3SJohn Marino     if (*fp)
1058*86d7f5d3SJohn Marino 	return SUCCESS;
1059*86d7f5d3SJohn Marino     else
1060*86d7f5d3SJohn Marino 	return FAILURE;
1061*86d7f5d3SJohn Marino }
1062*86d7f5d3SJohn Marino 
1063*86d7f5d3SJohn Marino static void
ai_unmapped(struct addrinfo * ai)1064*86d7f5d3SJohn Marino ai_unmapped(struct addrinfo *ai)
1065*86d7f5d3SJohn Marino {
1066*86d7f5d3SJohn Marino 	struct sockaddr_in6 *sin6;
1067*86d7f5d3SJohn Marino 	struct sockaddr_in sin;
1068*86d7f5d3SJohn Marino 
1069*86d7f5d3SJohn Marino 	if (ai->ai_family != AF_INET6)
1070*86d7f5d3SJohn Marino 		return;
1071*86d7f5d3SJohn Marino 	if (ai->ai_addrlen != sizeof(struct sockaddr_in6) ||
1072*86d7f5d3SJohn Marino 	    sizeof(sin) > ai->ai_addrlen)
1073*86d7f5d3SJohn Marino 		return;
1074*86d7f5d3SJohn Marino 	sin6 = (struct sockaddr_in6 *)ai->ai_addr;
1075*86d7f5d3SJohn Marino 	if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
1076*86d7f5d3SJohn Marino 		return;
1077*86d7f5d3SJohn Marino 
1078*86d7f5d3SJohn Marino 	memset(&sin, 0, sizeof(sin));
1079*86d7f5d3SJohn Marino 	sin.sin_family = AF_INET;
1080*86d7f5d3SJohn Marino 	sin.sin_len = sizeof(struct sockaddr_in);
1081*86d7f5d3SJohn Marino 	memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
1082*86d7f5d3SJohn Marino 	    sizeof(sin.sin_addr));
1083*86d7f5d3SJohn Marino 	sin.sin_port = sin6->sin6_port;
1084*86d7f5d3SJohn Marino 
1085*86d7f5d3SJohn Marino 	ai->ai_family = AF_INET;
1086*86d7f5d3SJohn Marino 	memcpy(ai->ai_addr, &sin, sin.sin_len);
1087*86d7f5d3SJohn Marino 	ai->ai_addrlen = sin.sin_len;
1088*86d7f5d3SJohn Marino }
1089