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