dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / ftp / ftp.c
blob6f25b838dd9a1b3b3e6f8c1d6250b101ebd3d416
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
40 #include "ftp_var.h"
41 #include <arpa/nameser.h>
42 #include <sys/types.h>
45 * WRITE() returns:
46 * >0 no error
47 * -1 error, errorno is set
48 * -2 security error (secure_write() only)
50 #define PUTC(x, y) secure_putc(x, y)
51 #define READ(x, y, z) secure_read(x, y, z)
52 #define WRITE(x, y, z) secure_write(x, y, z)
54 static struct sockaddr_in6 data_addr;
55 int data = -1;
56 static int abrtflag = 0;
57 static int ptflag = 0;
58 int connected;
59 static jmp_buf sendabort;
60 static jmp_buf recvabort;
61 static jmp_buf ptabort;
62 static int ptabflg;
63 static boolean_t pasv_refused;
64 boolean_t eport_supported = B_TRUE;
66 * For IPv6 addresses, EPSV will be the default (rather than EPRT/LPRT).
67 * The EPSV/ERPT ftp protocols are specified in RFC 2428.
69 * Perform EPSV if passivemode is set and ipv6rem is TRUE.
71 static boolean_t ipv6rem;
72 int use_eprt = 0; /* Testing option that specifies EPRT by default */
73 FILE *ctrl_in, *ctrl_out;
75 static void abortsend(int sig);
76 static void abortpt(int sig);
77 static void proxtrans(char *cmd, char *local, char *remote);
78 static void cmdabort(int sig);
79 static int empty(struct fd_set *mask, int sec, int nfds);
80 static void abortrecv(int sig);
81 static int initconn(void);
82 static FILE *dataconn(char *mode);
83 static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
84 hrtime_t t1, char *local, char *remote);
85 static void psabort(int sig);
86 static char *gunique(char *local);
87 static const char *inet_ntop_native(int af, const void *src, char *dst,
88 size_t size);
89 static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
91 static int secure_command(char *);
92 static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
94 static ssize_t bufcnt; /* number of bytes in buf[] */
95 static char *bufp; /* next character in buf */
96 static int buferr; /* last errno */
97 static size_t bufsize;
99 static void fdio_setbuf(char *buffer, size_t bufsize);
100 static int fdio_fillbuf(int fd);
101 static int fdio_error(int fd);
102 #define fdio_getc(fd) (--bufcnt < 0 ? fdio_fillbuf((fd)) : \
103 ((unsigned char)*bufp++))
105 #define MAX(a, b) ((a) > (b) ? (a) : (b))
106 #define NONZERO(x) ((x) == 0 ? 1 : (x))
108 static void
109 fdio_setbuf(char *buffer, size_t maxsize)
111 buf = buffer;
112 bufp = buf;
113 bufcnt = 0;
114 buferr = 0;
115 bufsize = maxsize;
118 static int
119 fdio_fillbuf(int fd)
121 bufcnt = timedread(fd, buf, bufsize, timeout);
122 if (bufcnt < 0)
123 buferr = errno;
124 if (bufcnt <= 0)
125 return (EOF);
126 bufp = buf;
127 bufcnt--;
128 return ((unsigned char)*bufp++);
132 * fdio_error - used on a file descriptor instead of ferror()
135 /*ARGSUSED*/
136 static int
137 fdio_error(int fd)
139 return (buferr);
143 * timedread - read buffer (like "read"), but with timeout (in seconds)
146 static ssize_t
147 timedread(int fd, void *buf, size_t size, int timeout)
149 struct fd_set mask;
150 struct timeval tv;
151 int err;
153 if (!timeout)
154 return (READ(fd, buf, size));
156 tv.tv_sec = (time_t)timeout;
157 tv.tv_usec = 0;
159 FD_ZERO(&mask);
160 FD_SET(fd, &mask);
162 err = select(fd + 1, &mask, NULL, NULL, &tv);
163 if (err == 0)
164 errno = ETIMEDOUT;
165 if (err <= 0)
166 return (-1);
168 return (READ(fd, buf, size));
172 char *
173 hookup(char *host, char *service)
175 struct addrinfo hints, *ai = NULL, *ai_head;
176 int s;
177 socklen_t len;
178 static char hostnamebuf[80];
179 struct in6_addr ipv6addr;
180 char abuf[INET6_ADDRSTRLEN];
181 int error_num;
182 int on = 1;
185 * There appears to be a bug in getaddrinfo() where, if the
186 * ai_family is set to AF_INET6, and the host is a v4-only
187 * host, getaddrinfo() returns an error instead of returning
188 * an v4-mapped ipv6 address. Therefore the ai_family is
189 * set to AF_UNSPEC and any returned v4 addresses are
190 * explicitly mapped within ftp.
192 bzero((char *)&remctladdr, sizeof (remctladdr));
193 bzero((char *)&hints, sizeof (hints));
194 hints.ai_flags = AI_CANONNAME;
195 hints.ai_family = AF_UNSPEC;
196 hints.ai_socktype = SOCK_STREAM;
198 error_num = getaddrinfo(host, service, &hints, &ai);
199 if (error_num != 0) {
200 if (error_num == EAI_AGAIN) {
201 (void) printf(
202 "%s: unknown host or invalid literal address "
203 "(try again later)\n", host);
204 } else {
205 (void) printf(
206 "%s: unknown host or invalid literal address\n",
207 host);
209 code = -1;
210 return (NULL);
212 ai_head = ai;
216 * If ai_canonname is a IPv4-mapped IPv6 literal, we'll convert it to
217 * IPv4 literal address.
219 if (ai->ai_canonname != NULL &&
220 (inet_pton(AF_INET6, ai->ai_canonname, &ipv6addr) > 0) &&
221 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
222 struct in_addr src4;
223 hostnamebuf[0] = '\0';
224 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &src4);
225 (void) inet_ntop(AF_INET, &src4, hostnamebuf,
226 sizeof (hostnamebuf));
229 * It can even be the case that the "host" supplied by the user
230 * can be a IPv4-mapped IPv6 literal. So, let's fix that too.
232 if ((inet_pton(AF_INET6, host, &ipv6addr) > 0) &&
233 IN6_IS_ADDR_V4MAPPED(&ipv6addr) &&
234 strlen(hostnamebuf) <= strlen(host)) {
235 (void) strlcpy(host, hostnamebuf, strlen(host) + 1);
237 } else {
238 reset_timer();
239 (void) strlcpy(hostnamebuf,
240 (ai->ai_canonname ? ai->ai_canonname : host),
241 sizeof (hostnamebuf));
244 hostname = hostnamebuf;
245 for (;;) {
246 int oerrno;
248 bcopy(ai->ai_addr, &remctladdr, ai->ai_addrlen);
249 if (ai->ai_addr->sa_family == AF_INET) {
250 IN6_INADDR_TO_V4MAPPED(
251 &(((struct sockaddr_in *)ai->ai_addr)->sin_addr),
252 &remctladdr.sin6_addr);
253 remctladdr.sin6_family = AF_INET6;
256 s = socket(AF_INET6, SOCK_STREAM, 0);
257 if (s < 0) {
258 perror("ftp: socket");
259 code = -1;
260 freeaddrinfo(ai_head);
261 return (0);
263 if (timeout && setsockopt(s, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
264 (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
265 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
266 reset_timer();
268 error_num = connect(s, (struct sockaddr *)&remctladdr,
269 sizeof (remctladdr));
270 oerrno = errno;
271 if (error_num >= 0)
272 break;
275 * Maintain message behavior: only include the address in
276 * our error message if we have another one to try; if this
277 * is the last address on our list, just print the error.
279 if (ai->ai_next != NULL) {
280 (void) fprintf(stderr, "ftp: connect to address %s: ",
281 inet_ntop_native(ai->ai_addr->sa_family,
282 (void *)ai->ai_addr, abuf, sizeof (abuf)));
283 errno = oerrno;
284 perror((char *)0);
285 } else {
286 perror("ftp: connect");
287 code = -1;
288 freeaddrinfo(ai_head);
289 goto bad;
291 ai = ai->ai_next;
292 (void) fprintf(stdout, "Trying %s...\n",
293 inet_ntop_native(ai->ai_addr->sa_family,
294 (void *)ai->ai_addr, abuf, sizeof (abuf)));
295 (void) close(s);
299 /* Set ipv6rem to TRUE if control connection is a native IPv6 address */
300 if (IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr))
301 ipv6rem = B_FALSE;
302 else
303 ipv6rem = B_TRUE;
306 freeaddrinfo(ai_head);
307 ai = NULL;
310 * Set passive mode flag on by default only if a native IPv6 address
311 * is being used -and- the use_eprt is not set.
313 if (ipv6rem == B_TRUE && use_eprt == 0)
314 passivemode = 1;
316 len = sizeof (myctladdr);
317 if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
318 perror("ftp: getsockname");
319 code = -1;
320 goto bad;
322 ctrl_in = fdopen(s, "r");
323 ctrl_out = fdopen(s, "w");
324 if (ctrl_in == NULL || ctrl_out == NULL) {
325 (void) fprintf(stderr, "ftp: fdopen failed.\n");
326 if (ctrl_in)
327 (void) fclose(ctrl_in);
328 if (ctrl_out)
329 (void) fclose(ctrl_out);
330 code = -1;
331 goto bad;
333 if (verbose)
334 (void) printf("Connected to %s.\n", hostname);
335 if (getreply(0) > 2) { /* read startup message from server */
336 if (ctrl_in)
337 (void) fclose(ctrl_in);
338 if (ctrl_out)
339 (void) fclose(ctrl_out);
340 ctrl_in = ctrl_out = NULL;
341 ctrl_in = ctrl_out = NULL;
342 code = -1;
343 goto bad;
345 if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
346 sizeof (on)) < 0 && debug)
347 perror("ftp: setsockopt (SO_OOBINLINE)");
349 return (hostname);
350 bad:
351 (void) close(s);
352 return (NULL);
356 login(char *host)
358 char tmp[80];
359 char *user, *pass, *acct;
360 int n, aflag = 0;
362 user = pass = acct = 0;
363 if (ruserpass(host, &user, &pass, &acct) < 0) {
364 disconnect(0, NULL);
365 code = -1;
366 return (0);
368 if (user == NULL) {
369 char *myname = getlogin();
371 if (myname == NULL) {
372 struct passwd *pp = getpwuid(getuid());
374 if (pp != NULL)
375 myname = pp->pw_name;
377 stop_timer();
378 (void) printf("Name (%s:%s): ", host,
379 (myname == NULL) ? "" : myname);
380 *tmp = '\0';
381 if (fgets(tmp, sizeof (tmp) - 1, stdin) != NULL)
382 tmp[strlen(tmp) - 1] = '\0';
383 if (*tmp != '\0')
384 user = tmp;
385 else if (myname != NULL)
386 user = myname;
387 else
388 return (0);
390 n = command("USER %s", user);
391 if (n == CONTINUE) {
392 int oldclevel;
393 if (pass == NULL)
394 pass = mygetpass("Password:");
395 oldclevel = clevel;
396 clevel = PROT_P;
397 n = command("PASS %s", pass);
398 /* level may have changed */
399 if (clevel == PROT_P)
400 clevel = oldclevel;
402 if (n == CONTINUE) {
403 aflag++;
404 if (acct == NULL)
405 acct = mygetpass("Account:");
406 n = command("ACCT %s", acct);
408 if (n != COMPLETE) {
409 (void) fprintf(stderr, "Login failed.\n");
410 return (0);
412 if (!aflag && acct != NULL)
413 (void) command("ACCT %s", acct);
414 if (proxy)
415 return (1);
416 for (n = 0; n < macnum; ++n) {
417 if (strcmp("init", macros[n].mac_name) == 0) {
418 (void) strlcpy(line, "$init", sizeof (line));
419 makeargv();
420 domacro(margc, margv);
421 break;
424 return (1);
427 /*ARGSUSED*/
428 static void
429 cmdabort(int sig)
431 (void) printf("\n");
432 (void) fflush(stdout);
433 abrtflag++;
434 if (ptflag)
435 longjmp(ptabort, 1);
439 command(char *fmt, ...)
441 int r;
442 void (*oldintr)();
443 va_list ap;
444 char command_buf[FTPBUFSIZ];
446 va_start(ap, fmt);
447 abrtflag = 0;
448 if (debug) {
449 (void) printf("---> ");
450 if (strncmp("PASS ", fmt, 5) == 0)
451 (void) printf("PASS XXXX");
452 else if (strncmp("ACCT ", fmt, 5) == 0)
453 (void) printf("ACCT XXXX");
454 else
455 (void) vfprintf(stdout, fmt, ap);
456 (void) printf("\n");
457 (void) fflush(stdout);
459 if (ctrl_out == NULL) {
460 perror("No control connection for command");
461 code = -1;
462 return (0);
464 oldintr = signal(SIGINT, cmdabort);
465 (void) vsnprintf(command_buf, FTPBUFSIZ, fmt, ap);
466 va_end(ap);
468 again: if (secure_command(command_buf) == 0)
469 return (0);
471 cpend = 1;
472 r = getreply(strcmp(fmt, "QUIT") == 0);
474 if (r == 533 && clevel == PROT_P) {
475 (void) fprintf(stderr, "ENC command not supported at server; "
476 "retrying under MIC...\n");
477 clevel = PROT_S;
478 goto again;
481 if (abrtflag && oldintr != SIG_IGN)
482 (*oldintr)();
483 (void) signal(SIGINT, oldintr);
484 return (r);
487 /* Need to save reply reponse from server for use in EPSV mode */
488 char reply_string[BUFSIZ];
491 getreply(int expecteof)
494 * 'code' is the 3 digit reply code, form xyz
495 * 'dig' counts the number of digits we are along in the code
496 * 'n' is the first digit of 'code'
497 * 4yz: resource unavailable
498 * 5yz: an error occurred, failure
499 * 6yz: protected reply (is_base64 == TRUE)
500 * 631 - base 64 encoded safe message
501 * 632 - base 64 encoded private message
502 * 633 - base 64 encoded confidential message
503 * 'c' is a wide char type, for international char sets
505 wint_t c;
506 int i, n;
507 int dig;
508 int originalcode = 0, continuation = 0;
509 void (*oldintr)();
510 int pflag = 0;
511 char *pt = pasv;
513 * this is the input and output buffers needed for
514 * radix_encode()
516 unsigned char ibuf[FTPBUFSIZ];
517 unsigned char obuf[FTPBUFSIZ];
518 boolean_t is_base64;
519 int len;
520 char *cp;
522 if (!ctrl_in)
523 return (0);
524 oldintr = signal(SIGINT, cmdabort);
526 ibuf[0] = '\0';
528 if (reply_parse)
529 reply_ptr = reply_buf;
531 for (;;) {
532 obuf[0] = '\0';
533 dig = n = code = 0;
534 i = is_base64 = 0;
535 cp = reply_string;
536 reset_timer(); /* once per line */
538 while ((c = ibuf[0] ?
539 (wint_t)ibuf[i++] : fgetwc(ctrl_in)) != '\n') {
541 if (i >= FTPBUFSIZ)
542 break;
544 if (c == IAC) { /* handle telnet commands */
545 switch (c = fgetwc(ctrl_in)) {
546 case WILL:
547 case WONT:
548 c = fgetwc(ctrl_in);
549 (void) fprintf(ctrl_out, "%c%c%wc", IAC,
550 WONT, c);
551 (void) fflush(ctrl_out);
552 break;
553 case DO:
554 case DONT:
555 c = fgetwc(ctrl_in);
556 (void) fprintf(ctrl_out, "%c%c%wc", IAC,
557 DONT, c);
558 (void) fflush(ctrl_out);
559 break;
560 default:
561 break;
563 continue;
565 dig++;
566 if (c == EOF) {
567 if (expecteof) {
568 (void) signal(SIGINT, oldintr);
569 code = 221;
570 return (0);
572 lostpeer(0);
573 if (verbose) {
574 (void) printf(
575 "421 Service not available, remote"
576 " server has closed connection\n");
577 } else
578 (void) printf("Lost connection\n");
579 (void) fflush(stdout);
580 code = 421;
581 return (4);
583 if (n == 0)
584 n = c;
586 if (n == '6')
587 is_base64 = 1;
589 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
590 (is_base64 || continuation)) {
591 /* start storing chars in obuf */
592 if (c != '\r' && dig > 4)
593 obuf[i++] = (char)c;
594 } else {
595 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
596 dig == 1 && verbose)
597 (void) printf("Unauthenticated reply received "
598 "from server:\n");
599 if (reply_parse)
600 *reply_ptr++ = (char)c;
601 if (c != '\r' && (verbose > 0 ||
602 (verbose > -1 && n == '5' && dig > 4))) {
603 if (proxflag &&
604 (dig == 1 || dig == 5 && verbose == 0))
605 (void) printf("%s:", hostname);
606 (void) putwchar(c);
608 } /* endif auth_type && !ibuf[0] ... */
610 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
611 continue;
613 /* we are still extracting the 3 digit code */
614 if (dig < 4 && isascii(c) && isdigit(c))
615 code = code * 10 + (c - '0');
617 /* starting passive mode */
618 if (!pflag && code == 227)
619 pflag = 1;
621 /* start to store characters, when dig > 4 */
622 if (dig > 4 && pflag == 1 && isascii(c) && isdigit(c))
623 pflag = 2;
624 if (pflag == 2) {
625 if (c != '\r' && c != ')') {
626 /* the mb array is to deal with the wchar_t */
627 char mb[MB_LEN_MAX];
628 int avail;
631 * space available in pasv[], accounting
632 * for trailing NULL
634 avail = &pasv[sizeof (pasv)] - pt - 1;
636 len = wctomb(mb, c);
637 if (len <= 0 && avail > 0) {
638 *pt++ = (unsigned char)c;
639 } else if (len > 0 && avail >= len) {
640 bcopy(mb, pt, (size_t)len);
641 pt += len;
642 } else {
644 * no room in pasv[];
645 * close connection
647 (void) printf("\nReply too long - "
648 "closing connection\n");
649 lostpeer(0);
650 (void) fflush(stdout);
651 (void) signal(SIGINT, oldintr);
652 return (4);
654 } else {
655 *pt = '\0';
656 pflag = 3;
658 } /* endif pflag == 2 */
659 if (dig == 4 && c == '-' && !is_base64) {
660 if (continuation)
661 code = 0;
662 continuation++;
664 if (cp < &reply_string[sizeof (reply_string) - 1])
665 *cp++ = c;
667 } /* end while */
669 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
670 return (getreply(expecteof));
672 ibuf[0] = obuf[i] = '\0';
674 if (code && is_base64) {
675 boolean_t again = 0;
676 n = decode_reply(ibuf, sizeof (ibuf), obuf, n, &again);
677 if (again)
678 continue;
679 } else
681 if (verbose > 0 || verbose > -1 && n == '5') {
682 (void) putwchar(c);
683 (void) fflush(stdout);
686 if (continuation && code != originalcode) {
687 ibuf[0] = obuf[i] = '\0';
688 if (originalcode == 0)
689 originalcode = code;
690 continue;
692 *cp = '\0';
693 if (n != '1')
694 cpend = 0;
695 (void) signal(SIGINT, oldintr);
696 if (code == 421 || originalcode == 421)
697 lostpeer(0);
698 if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
699 (*oldintr)();
701 if (reply_parse) {
702 *reply_ptr = '\0';
703 if (reply_ptr = strstr(reply_buf, reply_parse)) {
704 reply_parse = reply_ptr + strlen(reply_parse);
705 if (reply_ptr = strpbrk(reply_parse, " \r"))
706 *reply_ptr = '\0';
707 } else
708 reply_parse = reply_ptr;
711 return (n - '0');
712 } /* end for */
715 static int
716 empty(struct fd_set *mask, int sec, int nfds)
718 struct timeval t;
720 reset_timer();
721 t.tv_sec = (time_t)sec;
722 t.tv_usec = 0;
723 return (select(nfds, mask, NULL, NULL, &t));
726 /*ARGSUSED*/
727 static void
728 abortsend(int sig)
730 mflag = 0;
731 abrtflag = 0;
732 (void) printf("\nsend aborted\n");
733 (void) fflush(stdout);
734 longjmp(sendabort, 1);
737 void
738 sendrequest(char *cmd, char *local, char *remote, int allowpipe)
740 FILE *fin, *dout = 0;
741 int (*closefunc)();
742 void (*oldintr)(), (*oldintp)();
743 off_t bytes = 0, hashbytes = HASHSIZ;
744 int c;
746 * d >= 0 if there is no error
747 * -1 if there was a normal file i/o error
748 * -2 if there was a security error
750 int d;
751 struct stat st;
752 hrtime_t start, stop;
753 char *dmode;
755 if (proxy) {
756 proxtrans(cmd, local, remote);
757 return;
759 closefunc = NULL;
760 oldintr = NULL;
761 oldintp = NULL;
762 dmode = "w";
763 if (setjmp(sendabort)) {
764 while (cpend) {
765 (void) getreply(0);
767 if (data >= 0) {
768 (void) close(data);
769 data = -1;
771 if (oldintr)
772 (void) signal(SIGINT, oldintr);
773 if (oldintp)
774 (void) signal(SIGPIPE, oldintp);
775 code = -1;
776 restart_point = 0;
777 return;
779 oldintr = signal(SIGINT, abortsend);
780 if (strcmp(local, "-") == 0)
781 fin = stdin;
782 else if (allowpipe && *local == '|') {
783 oldintp = signal(SIGPIPE, SIG_IGN);
784 fin = mypopen(local + 1, "r");
785 if (fin == NULL) {
786 perror(local + 1);
787 (void) signal(SIGINT, oldintr);
788 (void) signal(SIGPIPE, oldintp);
789 code = -1;
790 restart_point = 0;
791 return;
793 closefunc = mypclose;
794 } else {
795 fin = fopen(local, "r");
796 if (fin == NULL) {
797 perror(local);
798 (void) signal(SIGINT, oldintr);
799 code = -1;
800 restart_point = 0;
801 return;
803 closefunc = fclose;
804 if (fstat(fileno(fin), &st) < 0 ||
805 (st.st_mode&S_IFMT) != S_IFREG) {
806 (void) fprintf(stdout,
807 "%s: not a plain file.\n", local);
808 (void) signal(SIGINT, oldintr);
809 code = -1;
810 (void) fclose(fin);
811 restart_point = 0;
812 return;
815 if (initconn()) {
816 (void) signal(SIGINT, oldintr);
817 if (oldintp)
818 (void) signal(SIGPIPE, oldintp);
819 code = -1;
820 if (closefunc != NULL)
821 (*closefunc)(fin);
822 restart_point = 0;
823 return;
825 if (setjmp(sendabort))
826 goto abort;
827 if ((restart_point > 0) &&
828 (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
829 if (fseeko(fin, restart_point, SEEK_SET) < 0) {
830 perror(local);
831 if (closefunc != NULL)
832 (*closefunc)(fin);
833 restart_point = 0;
834 return;
836 if (command("REST %lld", (longlong_t)restart_point)
837 != CONTINUE) {
838 if (closefunc != NULL)
839 (*closefunc)(fin);
840 restart_point = 0;
841 return;
843 dmode = "r+w";
845 restart_point = 0;
846 if (remote) {
847 if (command("%s %s", cmd, remote) != PRELIM) {
848 (void) signal(SIGINT, oldintr);
849 if (oldintp)
850 (void) signal(SIGPIPE, oldintp);
851 if (closefunc != NULL)
852 (*closefunc)(fin);
853 if (data >= 0) {
854 (void) close(data);
855 data = -1;
857 return;
859 } else
860 if (command("%s", cmd) != PRELIM) {
861 (void) signal(SIGINT, oldintr);
862 if (oldintp)
863 (void) signal(SIGPIPE, oldintp);
864 if (closefunc != NULL)
865 (*closefunc)(fin);
866 if (data >= 0) {
867 (void) close(data);
868 data = -1;
870 return;
872 dout = dataconn(dmode);
873 if (dout == NULL)
874 goto abort;
875 stop_timer();
876 oldintp = signal(SIGPIPE, SIG_IGN);
877 start = gethrtime();
878 switch (type) {
880 case TYPE_I:
881 case TYPE_L:
882 errno = d = 0;
883 while ((c = read(fileno(fin), buf, FTPBUFSIZ)) > 0) {
884 if ((d = WRITE(fileno(dout), buf, c)) < 0)
885 break;
886 bytes += c;
887 if (hash) {
888 while (bytes >= hashbytes) {
889 (void) putchar('#');
890 hashbytes += HASHSIZ;
892 (void) fflush(stdout);
895 if (hash && bytes > 0) {
896 if (bytes < hashbytes)
897 (void) putchar('#');
898 (void) putchar('\n');
899 (void) fflush(stdout);
901 if (c < 0)
902 perror(local);
904 if (d >= 0)
905 d = secure_flush(fileno(dout));
907 if (d < 0) {
908 if ((d == -1) && (errno != EPIPE))
909 perror("netout");
910 bytes = -1;
912 break;
914 case TYPE_A:
915 while ((c = getc(fin)) != EOF) {
916 if (c == '\n') {
917 while (hash && (bytes >= hashbytes)) {
918 (void) putchar('#');
919 (void) fflush(stdout);
920 hashbytes += HASHSIZ;
922 if (ferror(dout) || PUTC('\r', dout) < 0)
923 break;
924 bytes++;
927 if (PUTC(c, dout) < 0)
928 break;
929 bytes++;
930 #ifdef notdef
931 if (c == '\r') {
932 /* this violates rfc */
933 (void) PUTC('\0', dout);
934 bytes++;
936 #endif
938 if (hash && bytes > 0) {
939 if (bytes < hashbytes)
940 (void) putchar('#');
941 (void) putchar('\n');
942 (void) fflush(stdout);
944 if (ferror(fin))
945 perror(local);
947 d = ferror(dout) ? -1 : 0;
948 if (d == 0)
949 d = secure_flush(fileno(dout));
951 if (d < 0) {
952 if ((d == -1) && (errno != EPIPE))
953 perror("netout");
954 bytes = -1;
956 break;
958 reset_timer();
959 if (closefunc != NULL)
960 (*closefunc)(fin);
961 if (ctrl_in != NULL) {
962 int dfn = fileno(dout);
963 int nfds = fileno(ctrl_in);
964 fd_set mask;
967 * There could be data not yet written to dout,
968 * in the stdio buffer; so, before a shutdown()
969 * on further sends, do fflush(dout)
971 (void) fflush(dout);
973 /* sending over; shutdown sending on dfn */
974 (void) shutdown(dfn, SHUT_WR);
975 FD_ZERO(&mask);
976 FD_SET(dfn, &mask);
977 FD_SET(nfds, &mask);
978 nfds = MAX(dfn, nfds);
981 * Wait for remote end to either close data socket
982 * or ack that we've closed our end; it doesn't
983 * matter which happens first.
985 (void) select(nfds + 1, &mask, NULL, NULL, NULL);
987 (void) fclose(dout); data = -1;
988 stop = gethrtime();
989 (void) getreply(0);
990 (void) signal(SIGINT, oldintr);
991 if (oldintp)
992 (void) signal(SIGPIPE, oldintp);
995 * Only print the transfer successful message if the code returned
996 * from remote is 226 or 250. All other codes are error codes.
998 if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
999 ptransfer("sent", bytes, start, stop, local, remote);
1000 if (!ctrl_in)
1001 (void) printf("Lost connection\n");
1002 return;
1003 abort:
1004 (void) signal(SIGINT, oldintr);
1005 if (oldintp)
1006 (void) signal(SIGPIPE, oldintp);
1007 if (!cpend) {
1008 code = -1;
1009 return;
1011 if (data >= 0) {
1012 (void) close(data);
1013 data = -1;
1015 if (dout) {
1016 (void) fclose(dout);
1017 data = -1;
1019 (void) getreply(0);
1020 code = -1;
1021 if (closefunc != NULL && fin != NULL)
1022 (*closefunc)(fin);
1023 stop = gethrtime();
1025 * Only print the transfer successful message if the code returned
1026 * from remote is 226 or 250. All other codes are error codes.
1028 if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
1029 ptransfer("sent", bytes, start, stop, local, remote);
1030 if (!ctrl_in)
1031 (void) printf("Lost connection\n");
1032 restart_point = 0;
1035 /*ARGSUSED*/
1036 static void
1037 abortrecv(int sig)
1039 mflag = 0;
1040 abrtflag = 0;
1041 (void) printf("\n");
1042 (void) fflush(stdout);
1043 longjmp(recvabort, 1);
1046 void
1047 recvrequest(char *cmd, char *local, char *remote, char *mode, int allowpipe)
1049 FILE *fout, *din = 0;
1050 int (*closefunc)();
1051 void (*oldintr)(), (*oldintp)();
1052 int oldverbose, oldtype = 0, tcrflag, nfnd;
1053 char msg;
1054 off_t bytes = 0, hashbytes = HASHSIZ;
1055 struct fd_set mask;
1056 int c, d, n;
1057 hrtime_t start, stop;
1058 int errflg = 0;
1059 int infd;
1060 int nfds;
1061 int retrcmd;
1063 retrcmd = (strcmp(cmd, "RETR") == 0);
1064 if (proxy && retrcmd) {
1065 proxtrans(cmd, local, remote);
1066 return;
1068 closefunc = NULL;
1069 oldintr = NULL;
1070 oldintp = NULL;
1071 tcrflag = !crflag && retrcmd;
1072 if (setjmp(recvabort)) {
1073 while (cpend) {
1074 (void) getreply(0);
1076 if (data >= 0) {
1077 (void) close(data);
1078 data = -1;
1080 if (oldintr)
1081 (void) signal(SIGINT, oldintr);
1082 code = -1;
1083 return;
1085 oldintr = signal(SIGINT, abortrecv);
1086 if (local != NULL &&
1087 strcmp(local, "-") != 0 &&
1088 (*local != '|' || !allowpipe)) {
1089 if (access(local, W_OK) < 0) {
1090 char *dir = rindex(local, '/');
1091 int file_errno = errno;
1093 if (file_errno != ENOENT && file_errno != EACCES) {
1094 perror(local);
1095 (void) signal(SIGINT, oldintr);
1096 code = -1;
1097 return;
1099 if ((dir != NULL) && (dir != local))
1100 *dir = 0;
1101 if (dir == local)
1102 d = access("/", W_OK);
1103 else
1104 d = access(dir ? local : ".", W_OK);
1105 if ((dir != NULL) && (dir != local))
1106 *dir = '/';
1107 if (d < 0) {
1108 perror(local);
1109 (void) signal(SIGINT, oldintr);
1110 code = -1;
1111 return;
1113 if (!runique && file_errno == EACCES) {
1114 errno = file_errno;
1115 perror(local);
1116 (void) signal(SIGINT, oldintr);
1117 code = -1;
1118 return;
1120 if (runique && file_errno == EACCES &&
1121 (local = gunique(local)) == NULL) {
1122 (void) signal(SIGINT, oldintr);
1123 code = -1;
1124 return;
1126 } else if (runique && (local = gunique(local)) == NULL) {
1127 (void) signal(SIGINT, oldintr);
1128 code = -1;
1129 return;
1132 if (initconn()) {
1133 (void) signal(SIGINT, oldintr);
1134 code = -1;
1135 return;
1137 if (setjmp(recvabort))
1138 goto abort;
1139 if (!retrcmd && type != TYPE_A) {
1140 oldtype = type;
1141 oldverbose = verbose;
1142 if (!debug)
1143 verbose = 0;
1144 setascii(0, NULL);
1145 verbose = oldverbose;
1147 if ((restart_point > 0) && retrcmd &&
1148 command("REST %lld", (longlong_t)restart_point) != CONTINUE) {
1149 return;
1151 if (remote) {
1152 if (command("%s %s", cmd, remote) != PRELIM) {
1153 (void) signal(SIGINT, oldintr);
1154 if (oldtype) {
1155 if (!debug)
1156 verbose = 0;
1157 switch (oldtype) {
1158 case TYPE_I:
1159 setbinary(0, NULL);
1160 break;
1161 case TYPE_E:
1162 setebcdic(0, NULL);
1163 break;
1164 case TYPE_L:
1165 settenex(0, NULL);
1166 break;
1168 verbose = oldverbose;
1170 return;
1172 } else {
1173 if (command("%s", cmd) != PRELIM) {
1174 (void) signal(SIGINT, oldintr);
1175 if (oldtype) {
1176 if (!debug)
1177 verbose = 0;
1178 switch (oldtype) {
1179 case TYPE_I:
1180 setbinary(0, NULL);
1181 break;
1182 case TYPE_E:
1183 setebcdic(0, NULL);
1184 break;
1185 case TYPE_L:
1186 settenex(0, NULL);
1187 break;
1189 verbose = oldverbose;
1191 return;
1194 din = dataconn("r");
1195 if (din == NULL)
1196 goto abort;
1198 if (local == NULL) {
1199 fout = tmp_nlst;
1200 } else if (strcmp(local, "-") == 0) {
1201 fout = stdout;
1202 } else if (allowpipe && *local == '|') {
1203 oldintp = signal(SIGPIPE, SIG_IGN);
1204 fout = mypopen(local + 1, "w");
1205 if (fout == NULL) {
1206 perror(local+1);
1207 goto abort;
1209 closefunc = mypclose;
1210 } else {
1211 fout = fopen(local, mode);
1212 if (fout == NULL) {
1213 perror(local);
1214 goto abort;
1216 closefunc = fclose;
1218 start = gethrtime();
1219 stop_timer();
1220 switch (type) {
1222 case TYPE_I:
1223 case TYPE_L:
1224 if ((restart_point > 0) && retrcmd &&
1225 lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1226 perror(local);
1227 goto abort;
1229 errno = d = 0;
1230 infd = fileno(din);
1231 while ((c = timedread(infd, buf, FTPBUFSIZ, timeout)) > 0) {
1232 for (n = 0; n < c; n += d) {
1233 d = write(fileno(fout), &buf[n], c - n);
1234 if (d == -1)
1235 goto writeerr;
1237 bytes += c;
1238 if (hash) {
1239 while (bytes >= hashbytes) {
1240 (void) putchar('#');
1241 hashbytes += HASHSIZ;
1243 (void) fflush(stdout);
1246 if (hash && bytes > 0) {
1247 if (bytes < hashbytes)
1248 (void) putchar('#');
1249 (void) putchar('\n');
1250 (void) fflush(stdout);
1252 if (c < 0) {
1253 errflg = 1;
1254 perror("netin");
1256 if ((d < 0) || ((c == 0) && (fsync(fileno(fout)) == -1))) {
1257 writeerr:
1258 errflg = 1;
1259 perror(local);
1261 break;
1263 case TYPE_A:
1264 if ((restart_point > 0) && retrcmd) {
1265 int c;
1266 off_t i = 0;
1268 if (fseek(fout, 0L, SEEK_SET) < 0) {
1269 perror(local);
1270 goto abort;
1272 while (i++ < restart_point) {
1273 if ((c = getc(fout)) == EOF) {
1274 if (ferror(fout))
1275 perror(local);
1276 else
1277 (void) fprintf(stderr,
1278 "%s: Unexpected end of file\n",
1279 local);
1280 goto abort;
1282 if (c == '\n')
1283 i++;
1285 if (fseeko(fout, 0L, SEEK_CUR) < 0) {
1286 perror(local);
1287 goto abort;
1290 fdio_setbuf(buf, FTPBUFSIZ);
1291 infd = fileno(din);
1292 while ((c = fdio_getc(infd)) != EOF) {
1293 while (c == '\r') {
1294 while (hash && (bytes >= hashbytes)) {
1295 (void) putchar('#');
1296 (void) fflush(stdout);
1297 hashbytes += HASHSIZ;
1299 bytes++;
1301 if ((c = fdio_getc(infd)) != '\n' || tcrflag) {
1302 if (ferror(fout))
1303 break;
1304 if (putc('\r', fout) == EOF)
1305 goto writer_ascii_err;
1307 #ifdef notdef
1308 if (c == '\0') {
1309 bytes++;
1310 continue;
1312 #endif
1313 if (c == EOF)
1314 goto endread;
1316 if (putc(c, fout) == EOF)
1317 goto writer_ascii_err;
1318 bytes++;
1320 endread:
1321 if (hash && bytes > 0) {
1322 if (bytes < hashbytes)
1323 (void) putchar('#');
1324 (void) putchar('\n');
1325 (void) fflush(stdout);
1327 if (fdio_error(infd)) {
1328 errflg = 1;
1329 perror("netin");
1331 if ((fflush(fout) == EOF) || ferror(fout) ||
1332 (fsync(fileno(fout)) == -1)) {
1333 writer_ascii_err:
1334 errflg = 1;
1335 perror(local);
1337 break;
1339 reset_timer();
1340 if (closefunc != NULL)
1341 (*closefunc)(fout);
1342 (void) signal(SIGINT, oldintr);
1343 if (oldintp)
1344 (void) signal(SIGPIPE, oldintp);
1345 (void) fclose(din); data = -1;
1346 stop = gethrtime();
1347 (void) getreply(0);
1348 if (bytes > 0 && verbose && !errflg)
1349 ptransfer("received", bytes, start, stop, local, remote);
1350 if (!ctrl_in)
1351 (void) printf("Lost connection\n");
1352 if (oldtype) {
1353 if (!debug)
1354 verbose = 0;
1355 switch (oldtype) {
1356 case TYPE_I:
1357 setbinary(0, NULL);
1358 break;
1359 case TYPE_E:
1360 setebcdic(0, NULL);
1361 break;
1362 case TYPE_L:
1363 settenex(0, NULL);
1364 break;
1366 verbose = oldverbose;
1368 return;
1369 abort:
1371 /* abort using RFC959 recommended IP, SYNC sequence */
1373 stop = gethrtime();
1374 if (oldintp)
1375 (void) signal(SIGPIPE, oldintp);
1376 (void) signal(SIGINT, SIG_IGN);
1377 if (!cpend) {
1378 code = -1;
1379 (void) signal(SIGINT, oldintr);
1380 return;
1383 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
1384 (void) fflush(ctrl_out);
1385 msg = (char)IAC;
1387 * send IAC in urgent mode instead of DM because UNIX places oob
1388 * mark after urgent byte rather than before as now is protocol
1390 if (send(fileno(ctrl_out), &msg, 1, MSG_OOB) != 1) {
1391 perror("abort");
1393 (void) fprintf(ctrl_out, "%cABOR\r\n", DM);
1394 (void) fflush(ctrl_out);
1395 nfds = fileno(ctrl_in) + 1;
1396 FD_ZERO(&mask);
1397 FD_SET(fileno(ctrl_in), &mask);
1398 if (din) {
1399 FD_SET(fileno(din), &mask);
1400 nfds = MAX(fileno(din) + 1, nfds);
1402 if ((nfnd = empty(&mask, 10, nfds)) <= 0) {
1403 if (nfnd < 0) {
1404 perror("abort");
1406 code = -1;
1407 lostpeer(0);
1409 if (din && FD_ISSET(fileno(din), &mask)) {
1410 do {
1411 reset_timer();
1412 } while ((c = read(fileno(din), buf, FTPBUFSIZ)) > 0);
1414 if ((c = getreply(0)) == ERROR && code == 552) {
1415 /* needed for nic style abort */
1416 if (data >= 0) {
1417 (void) close(data);
1418 data = -1;
1420 (void) getreply(0);
1422 if (oldtype) {
1423 if (!debug)
1424 verbose = 0;
1425 switch (oldtype) {
1426 case TYPE_I:
1427 setbinary(0, NULL);
1428 break;
1429 case TYPE_E:
1430 setebcdic(0, NULL);
1431 break;
1432 case TYPE_L:
1433 settenex(0, NULL);
1434 break;
1436 verbose = oldverbose;
1438 (void) getreply(0);
1439 code = -1;
1440 if (data >= 0) {
1441 (void) close(data);
1442 data = -1;
1444 if (closefunc != NULL && fout != NULL)
1445 (*closefunc)(fout);
1446 if (din) {
1447 (void) fclose(din);
1448 data = -1;
1450 if (bytes > 0 && verbose)
1451 ptransfer("received", bytes, start, stop, local, remote);
1452 if (!ctrl_in)
1453 (void) printf("Lost connection\n");
1454 (void) signal(SIGINT, oldintr);
1458 * Need to start a listen on the data channel
1459 * before we send the command, otherwise the
1460 * server's connect may fail.
1463 static int
1464 initconn(void)
1466 unsigned char *p, *a;
1467 int result, tmpno = 0;
1468 int on = 1;
1469 socklen_t len;
1470 int v4_addr;
1471 char *c, *c2, delm;
1472 in_port_t ports;
1474 pasv_refused = B_FALSE;
1475 if (passivemode) {
1476 data = socket(AF_INET6, SOCK_STREAM, 0);
1477 if (data < 0) {
1478 perror("socket");
1479 return (1);
1481 if (timeout && setsockopt(data, IPPROTO_TCP,
1482 TCP_ABORT_THRESHOLD, (char *)&timeoutms,
1483 sizeof (timeoutms)) < 0 && debug)
1484 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
1485 if ((options & SO_DEBUG) &&
1486 setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
1487 sizeof (on)) < 0)
1488 perror("setsockopt (ignored)");
1490 * Use the system wide default send and receive buffer sizes
1491 * unless one has been specified.
1493 if (tcpwindowsize) {
1494 if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
1495 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1496 perror("ftp: setsockopt (SO_SNDBUF - ignored)");
1497 if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
1498 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1499 perror("ftp: setsockopt (SO_RCVBUF - ignored)");
1502 data_addr = remctladdr;
1504 if (ipv6rem == B_TRUE) {
1505 if (command("EPSV") != COMPLETE) {
1506 (void) fprintf(stderr,
1507 "Passive mode refused. Try EPRT\n");
1508 pasv_refused = B_TRUE;
1509 goto noport;
1513 * Get the data port from reply string from the
1514 * server. The format of the reply string is:
1515 * 229 Entering Extended Passive Mode (|||port|)
1516 * where | is the delimiter being used.
1518 c = strchr(reply_string, '(');
1519 c2 = strchr(reply_string, ')');
1520 if (c == NULL || c2 == NULL) {
1521 (void) fprintf(stderr, "Extended passive mode"
1522 "parsing failure.\n");
1523 goto bad;
1525 *(c2 - 1) = '\0';
1526 /* Delimiter is the next char in the reply string */
1527 delm = *(++c);
1528 while (*c == delm) {
1529 if (!*(c++)) {
1530 (void) fprintf(stderr,
1531 "Extended passive mode"
1532 "parsing failure.\n");
1533 goto bad;
1536 /* assign the port for data connection */
1537 ports = (in_port_t)atoi(c);
1538 data_addr.sin6_port = htons(ports);
1539 } else {
1540 int a1, a2, a3, a4, p1, p2;
1542 if (command("PASV") != COMPLETE) {
1543 (void) fprintf(stderr,
1544 "Passive mode refused. Try PORT\n");
1545 pasv_refused = B_TRUE;
1546 goto noport;
1550 * Get the data port from reply string from the
1551 * server. The format of the reply string is:
1552 * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
1554 if (sscanf(pasv, "%d,%d,%d,%d,%d,%d",
1555 &a1, &a2, &a3, &a4, &p1, &p2) != 6) {
1556 (void) fprintf(stderr,
1557 "Passive mode parsing failure.\n");
1558 goto bad;
1561 * Set the supplied address and port in an
1562 * IPv4-mapped IPv6 address.
1564 a = (unsigned char *)&data_addr.sin6_addr +
1565 sizeof (struct in6_addr) -
1566 sizeof (struct in_addr);
1567 #define UC(b) ((b)&0xff)
1568 a[0] = UC(a1);
1569 a[1] = UC(a2);
1570 a[2] = UC(a3);
1571 a[3] = UC(a4);
1572 p = (unsigned char *)&data_addr.sin6_port;
1573 p[0] = UC(p1);
1574 p[1] = UC(p2);
1577 if (connect(data, (struct sockaddr *)&data_addr,
1578 sizeof (data_addr)) < 0) {
1579 perror("connect");
1580 goto bad;
1582 return (0);
1585 noport:
1586 data_addr = myctladdr;
1587 if (sendport)
1588 data_addr.sin6_port = 0; /* let system pick one */
1590 if (data != -1)
1591 (void) close(data);
1592 data = socket(AF_INET6, SOCK_STREAM, 0);
1593 if (data < 0) {
1594 perror("ftp: socket");
1595 if (tmpno)
1596 sendport = 1;
1597 return (1);
1599 if (!sendport)
1600 if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
1601 (char *)&on, sizeof (on)) < 0) {
1602 perror("ftp: setsockopt (SO_REUSEADDR)");
1603 goto bad;
1605 if (bind(data,
1606 (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
1607 perror("ftp: bind");
1608 goto bad;
1610 if (timeout && setsockopt(data, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
1611 (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
1612 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
1613 if (options & SO_DEBUG &&
1614 setsockopt(data, SOL_SOCKET, SO_DEBUG,
1615 (char *)&on, sizeof (on)) < 0)
1616 perror("ftp: setsockopt (SO_DEBUG - ignored)");
1618 * Use the system wide default send and receive buffer sizes unless
1619 * one has been specified.
1621 if (tcpwindowsize) {
1622 if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
1623 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1624 perror("ftp: setsockopt (SO_SNDBUF - ignored)");
1625 if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
1626 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1627 perror("ftp: setsockopt (SO_RCVBUF - ignored)");
1629 len = sizeof (data_addr);
1630 if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
1631 perror("ftp: getsockname");
1632 goto bad;
1635 v4_addr = IN6_IS_ADDR_V4MAPPED(&data_addr.sin6_addr);
1636 if (listen(data, 1) < 0)
1637 perror("ftp: listen");
1639 if (sendport) {
1640 a = (unsigned char *)&data_addr.sin6_addr;
1641 p = (unsigned char *)&data_addr.sin6_port;
1642 if (v4_addr) {
1643 result =
1644 command("PORT %d,%d,%d,%d,%d,%d",
1645 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1646 UC(p[0]), UC(p[1]));
1647 } else {
1648 char hname[INET6_ADDRSTRLEN];
1650 result = COMPLETE + 1;
1652 * if on previous try to server, it was
1653 * determined that the server doesn't support
1654 * EPRT, don't bother trying again. Just try
1655 * LPRT.
1657 if (eport_supported == B_TRUE) {
1658 if (inet_ntop(AF_INET6, &data_addr.sin6_addr,
1659 hname, sizeof (hname)) != NULL) {
1660 result = command("EPRT |%d|%s|%d|", 2,
1661 hname, htons(data_addr.sin6_port));
1662 if (result != COMPLETE)
1663 eport_supported = B_FALSE;
1666 /* Try LPRT */
1667 if (result != COMPLETE) {
1668 result = command(
1669 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1670 6, 16,
1671 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1672 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
1673 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
1674 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1675 2, UC(p[0]), UC(p[1]));
1679 if (result == ERROR && sendport == -1) {
1680 sendport = 0;
1681 tmpno = 1;
1682 goto noport;
1684 return (result != COMPLETE);
1686 if (tmpno)
1687 sendport = 1;
1688 return (0);
1689 bad:
1690 (void) close(data), data = -1;
1691 if (tmpno)
1692 sendport = 1;
1693 return (1);
1696 static FILE *
1697 dataconn(char *mode)
1699 struct sockaddr_in6 from;
1700 int s;
1701 socklen_t fromlen = sizeof (from);
1703 reset_timer();
1704 if (passivemode && !pasv_refused)
1705 return (fdopen(data, mode));
1707 s = accept(data, (struct sockaddr *)&from, &fromlen);
1708 if (s < 0) {
1709 perror("ftp: accept");
1710 (void) close(data), data = -1;
1711 return (NULL);
1713 (void) close(data);
1714 data = s;
1715 return (fdopen(data, mode));
1718 static void
1719 ptransfer(char *direction, off_t bytes, hrtime_t t0,
1720 hrtime_t t1, char *local, char *remote)
1722 hrtime_t td; /* nanoseconds in a 64 bit int */
1723 double s, bs;
1725 td = t1 - t0;
1726 s = (double)td / 1000000000.0; /* seconds */
1727 bs = (double)bytes / NONZERO(s);
1728 if (local && *local != '-')
1729 (void) printf("local: %s ", local);
1730 if (remote)
1731 (void) printf("remote: %s\n", remote);
1732 (void) printf("%lld bytes %s in %.2g seconds (%.2f Kbytes/s)\n",
1733 (longlong_t)bytes, direction, s, bs / 1024.0);
1736 /*ARGSUSED*/
1737 static void
1738 psabort(int sig)
1740 abrtflag++;
1743 void
1744 pswitch(int flag)
1746 void (*oldintr)();
1747 static struct comvars {
1748 int connect;
1749 char name[MAXHOSTNAMELEN];
1750 struct sockaddr_in6 mctl;
1751 struct sockaddr_in6 hctl;
1752 FILE *in;
1753 FILE *out;
1754 int tpe;
1755 int cpnd;
1756 int sunqe;
1757 int runqe;
1758 int mcse;
1759 int ntflg;
1760 char nti[17];
1761 char nto[17];
1762 int mapflg;
1763 char mi[MAXPATHLEN];
1764 char mo[MAXPATHLEN];
1765 int authtype;
1766 int clvl;
1767 int dlvl;
1768 } proxstruct, tmpstruct;
1769 struct comvars *ip, *op;
1771 abrtflag = 0;
1772 oldintr = signal(SIGINT, psabort);
1773 if (flag) {
1774 if (proxy)
1775 return;
1776 ip = &tmpstruct;
1777 op = &proxstruct;
1778 proxy++;
1779 } else {
1780 if (!proxy)
1781 return;
1782 ip = &proxstruct;
1783 op = &tmpstruct;
1784 proxy = 0;
1786 ip->connect = connected;
1787 connected = op->connect;
1788 if (hostname)
1789 (void) strlcpy(ip->name, hostname, sizeof (ip->name));
1790 else
1791 ip->name[0] = 0;
1792 hostname = op->name;
1793 ip->hctl = remctladdr;
1794 remctladdr = op->hctl;
1795 ip->mctl = myctladdr;
1796 myctladdr = op->mctl;
1797 ip->in = ctrl_in;
1798 ctrl_in = op->in;
1799 ip->out = ctrl_out;
1800 ctrl_out = op->out;
1801 ip->tpe = type;
1802 type = op->tpe;
1803 if (!type)
1804 type = 1;
1805 ip->cpnd = cpend;
1806 cpend = op->cpnd;
1807 ip->sunqe = sunique;
1808 sunique = op->sunqe;
1809 ip->runqe = runique;
1810 runique = op->runqe;
1811 ip->mcse = mcase;
1812 mcase = op->mcse;
1813 ip->ntflg = ntflag;
1814 ntflag = op->ntflg;
1815 (void) strlcpy(ip->nti, ntin, sizeof (ip->nti));
1816 (void) strlcpy(ntin, op->nti, sizeof (ntin));
1817 (void) strlcpy(ip->nto, ntout, sizeof (ip->nto));
1818 (void) strlcpy(ntout, op->nto, sizeof (ntout));
1819 ip->mapflg = mapflag;
1820 mapflag = op->mapflg;
1821 (void) strlcpy(ip->mi, mapin, sizeof (ip->mi));
1822 (void) strlcpy(mapin, op->mi, sizeof (mapin));
1823 (void) strlcpy(ip->mo, mapout, sizeof (ip->mo));
1824 (void) strlcpy(mapout, op->mo, sizeof (mapout));
1826 ip->authtype = auth_type;
1827 auth_type = op->authtype;
1828 ip->clvl = clevel;
1829 clevel = op->clvl;
1830 ip->dlvl = dlevel;
1831 dlevel = op->dlvl;
1832 if (!clevel)
1833 clevel = PROT_C;
1834 if (!dlevel)
1835 dlevel = PROT_C;
1837 (void) signal(SIGINT, oldintr);
1838 if (abrtflag) {
1839 abrtflag = 0;
1840 (*oldintr)();
1844 /*ARGSUSED*/
1845 static void
1846 abortpt(int sig)
1848 (void) printf("\n");
1849 (void) fflush(stdout);
1850 ptabflg++;
1851 mflag = 0;
1852 abrtflag = 0;
1853 longjmp(ptabort, 1);
1856 static void
1857 proxtrans(char *cmd, char *local, char *remote)
1859 void (*oldintr)();
1860 int tmptype, oldtype = 0, secndflag = 0, nfnd;
1861 extern jmp_buf ptabort;
1862 char *cmd2;
1863 struct fd_set mask;
1864 int ipv4_addr = IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr);
1866 if (strcmp(cmd, "RETR"))
1867 cmd2 = "RETR";
1868 else
1869 cmd2 = runique ? "STOU" : "STOR";
1870 if (command(ipv4_addr ? "PASV" : "EPSV") != COMPLETE) {
1871 (void) printf(
1872 "proxy server does not support third part transfers.\n");
1873 return;
1875 tmptype = type;
1876 pswitch(0);
1877 if (!connected) {
1878 (void) printf("No primary connection\n");
1879 pswitch(1);
1880 code = -1;
1881 return;
1883 if (type != tmptype) {
1884 oldtype = type;
1885 switch (tmptype) {
1886 case TYPE_A:
1887 setascii(0, NULL);
1888 break;
1889 case TYPE_I:
1890 setbinary(0, NULL);
1891 break;
1892 case TYPE_E:
1893 setebcdic(0, NULL);
1894 break;
1895 case TYPE_L:
1896 settenex(0, NULL);
1897 break;
1900 if (command(ipv4_addr ? "PORT %s" : "EPRT %s", pasv) != COMPLETE) {
1901 switch (oldtype) {
1902 case 0:
1903 break;
1904 case TYPE_A:
1905 setascii(0, NULL);
1906 break;
1907 case TYPE_I:
1908 setbinary(0, NULL);
1909 break;
1910 case TYPE_E:
1911 setebcdic(0, NULL);
1912 break;
1913 case TYPE_L:
1914 settenex(0, NULL);
1915 break;
1917 pswitch(1);
1918 return;
1920 if (setjmp(ptabort))
1921 goto abort;
1922 oldintr = signal(SIGINT, (void (*)())abortpt);
1923 if (command("%s %s", cmd, remote) != PRELIM) {
1924 (void) signal(SIGINT, oldintr);
1925 switch (oldtype) {
1926 case 0:
1927 break;
1928 case TYPE_A:
1929 setascii(0, NULL);
1930 break;
1931 case TYPE_I:
1932 setbinary(0, NULL);
1933 break;
1934 case TYPE_E:
1935 setebcdic(0, NULL);
1936 break;
1937 case TYPE_L:
1938 settenex(0, NULL);
1939 break;
1941 pswitch(1);
1942 return;
1944 (void) sleep(2);
1945 pswitch(1);
1946 secndflag++;
1947 if (command("%s %s", cmd2, local) != PRELIM)
1948 goto abort;
1949 ptflag++;
1950 (void) getreply(0);
1951 pswitch(0);
1952 (void) getreply(0);
1953 (void) signal(SIGINT, oldintr);
1954 switch (oldtype) {
1955 case 0:
1956 break;
1957 case TYPE_A:
1958 setascii(0, NULL);
1959 break;
1960 case TYPE_I:
1961 setbinary(0, NULL);
1962 break;
1963 case TYPE_E:
1964 setebcdic(0, NULL);
1965 break;
1966 case TYPE_L:
1967 settenex(0, NULL);
1968 break;
1970 pswitch(1);
1971 ptflag = 0;
1972 (void) printf("local: %s remote: %s\n", local, remote);
1973 return;
1974 abort:
1975 (void) signal(SIGINT, SIG_IGN);
1976 ptflag = 0;
1977 if (strcmp(cmd, "RETR") && !proxy)
1978 pswitch(1);
1979 else if ((strcmp(cmd, "RETR") == 0) && proxy)
1980 pswitch(0);
1981 if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
1982 if (command("%s %s", cmd2, local) != PRELIM) {
1983 pswitch(0);
1984 switch (oldtype) {
1985 case 0:
1986 break;
1987 case TYPE_A:
1988 setascii(0, NULL);
1989 break;
1990 case TYPE_I:
1991 setbinary(0, NULL);
1992 break;
1993 case TYPE_E:
1994 setebcdic(0, NULL);
1995 break;
1996 case TYPE_L:
1997 settenex(0, NULL);
1998 break;
2000 if (cpend) {
2001 char msg[2];
2003 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2004 (void) fflush(ctrl_out);
2005 *msg = (char)IAC;
2006 *(msg+1) = (char)DM;
2007 if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
2008 != 2)
2009 perror("abort");
2010 (void) fprintf(ctrl_out, "ABOR\r\n");
2011 (void) fflush(ctrl_out);
2012 FD_ZERO(&mask);
2013 FD_SET(fileno(ctrl_in), &mask);
2014 if ((nfnd = empty(&mask, 10,
2015 fileno(ctrl_in) + 1)) <= 0) {
2016 if (nfnd < 0) {
2017 perror("abort");
2019 if (ptabflg)
2020 code = -1;
2021 lostpeer(0);
2023 (void) getreply(0);
2024 (void) getreply(0);
2027 pswitch(1);
2028 if (ptabflg)
2029 code = -1;
2030 (void) signal(SIGINT, oldintr);
2031 return;
2033 if (cpend) {
2034 char msg[2];
2036 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2037 (void) fflush(ctrl_out);
2038 *msg = (char)IAC;
2039 *(msg+1) = (char)DM;
2040 if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
2041 perror("abort");
2042 (void) fprintf(ctrl_out, "ABOR\r\n");
2043 (void) fflush(ctrl_out);
2044 FD_ZERO(&mask);
2045 FD_SET(fileno(ctrl_in), &mask);
2046 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2047 if (nfnd < 0) {
2048 perror("abort");
2050 if (ptabflg)
2051 code = -1;
2052 lostpeer(0);
2054 (void) getreply(0);
2055 (void) getreply(0);
2057 pswitch(!proxy);
2058 if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
2059 if (command("%s %s", cmd2, local) != PRELIM) {
2060 pswitch(0);
2061 switch (oldtype) {
2062 case 0:
2063 break;
2064 case TYPE_A:
2065 setascii(0, NULL);
2066 break;
2067 case TYPE_I:
2068 setbinary(0, NULL);
2069 break;
2070 case TYPE_E:
2071 setebcdic(0, NULL);
2072 break;
2073 case TYPE_L:
2074 settenex(0, NULL);
2075 break;
2077 if (cpend) {
2078 char msg[2];
2080 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2081 (void) fflush(ctrl_out);
2082 *msg = (char)IAC;
2083 *(msg+1) = (char)DM;
2084 if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
2085 != 2)
2086 perror("abort");
2087 (void) fprintf(ctrl_out, "ABOR\r\n");
2088 (void) fflush(ctrl_out);
2089 FD_ZERO(&mask);
2090 FD_SET(fileno(ctrl_in), &mask);
2091 if ((nfnd = empty(&mask, 10,
2092 fileno(ctrl_in) + 1)) <= 0) {
2093 if (nfnd < 0) {
2094 perror("abort");
2096 if (ptabflg)
2097 code = -1;
2098 lostpeer(0);
2100 (void) getreply(0);
2101 (void) getreply(0);
2103 pswitch(1);
2104 if (ptabflg)
2105 code = -1;
2106 (void) signal(SIGINT, oldintr);
2107 return;
2110 if (cpend) {
2111 char msg[2];
2113 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2114 (void) fflush(ctrl_out);
2115 *msg = (char)IAC;
2116 *(msg+1) = (char)DM;
2117 if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
2118 perror("abort");
2119 (void) fprintf(ctrl_out, "ABOR\r\n");
2120 (void) fflush(ctrl_out);
2121 FD_ZERO(&mask);
2122 FD_SET(fileno(ctrl_in), &mask);
2123 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2124 if (nfnd < 0) {
2125 perror("abort");
2127 if (ptabflg)
2128 code = -1;
2129 lostpeer(0);
2131 (void) getreply(0);
2132 (void) getreply(0);
2134 pswitch(!proxy);
2135 if (cpend) {
2136 FD_ZERO(&mask);
2137 FD_SET(fileno(ctrl_in), &mask);
2138 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2139 if (nfnd < 0) {
2140 perror("abort");
2142 if (ptabflg)
2143 code = -1;
2144 lostpeer(0);
2146 (void) getreply(0);
2147 (void) getreply(0);
2149 if (proxy)
2150 pswitch(0);
2151 switch (oldtype) {
2152 case 0:
2153 break;
2154 case TYPE_A:
2155 setascii(0, NULL);
2156 break;
2157 case TYPE_I:
2158 setbinary(0, NULL);
2159 break;
2160 case TYPE_E:
2161 setebcdic(0, NULL);
2162 break;
2163 case TYPE_L:
2164 settenex(0, NULL);
2165 break;
2167 pswitch(1);
2168 if (ptabflg)
2169 code = -1;
2170 (void) signal(SIGINT, oldintr);
2173 /*ARGSUSED*/
2174 void
2175 reset(int argc, char *argv[])
2177 struct fd_set mask;
2178 int nfnd = 1;
2180 FD_ZERO(&mask);
2181 while (nfnd > 0) {
2182 FD_SET(fileno(ctrl_in), &mask);
2183 if ((nfnd = empty(&mask, 0, fileno(ctrl_in) + 1)) < 0) {
2184 perror("reset");
2185 code = -1;
2186 lostpeer(0);
2187 } else if (nfnd > 0) {
2188 (void) getreply(0);
2193 static char *
2194 gunique(char *local)
2196 static char new[MAXPATHLEN];
2197 char *cp = rindex(local, '/');
2198 int d, count = 0;
2199 char ext = '1';
2201 if (cp)
2202 *cp = '\0';
2203 d = access(cp ? local : ".", 2);
2204 if (cp)
2205 *cp = '/';
2206 if (d < 0) {
2207 perror(local);
2208 return (NULL);
2210 if (strlcpy(new, local, sizeof (new)) >= sizeof (new))
2211 (void) printf("gunique: too long: local %s, %d, new %d\n",
2212 local, strlen(local), sizeof (new));
2214 cp = new + strlen(new);
2215 *cp++ = '.';
2216 while (!d) {
2217 if (++count == 100) {
2218 (void) printf(
2219 "runique: can't find unique file name.\n");
2220 return (NULL);
2222 *cp++ = ext;
2223 *cp = '\0';
2224 if (ext == '9')
2225 ext = '0';
2226 else
2227 ext++;
2228 if ((d = access(new, 0)) < 0)
2229 break;
2230 if (ext != '0')
2231 cp--;
2232 else if (*(cp - 2) == '.')
2233 *(cp - 1) = '1';
2234 else {
2235 *(cp - 2) = *(cp - 2) + 1;
2236 cp--;
2239 return (new);
2243 * This is a wrap-around function for inet_ntop(). In case the af is AF_INET6
2244 * and the address pointed by src is a IPv4-mapped IPv6 address, it
2245 * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
2246 * it behaves just like inet_ntop().
2248 const char *
2249 inet_ntop_native(int af, const void *src, char *dst, size_t size)
2251 struct in_addr src4;
2252 const char *result;
2253 struct sockaddr_in *sin;
2254 struct sockaddr_in6 *sin6;
2256 if (af == AF_INET6) {
2257 sin6 = (struct sockaddr_in6 *)src;
2258 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
2259 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &src4);
2260 result = inet_ntop(AF_INET, &src4, dst, size);
2261 } else {
2262 result = inet_ntop(AF_INET6, &sin6->sin6_addr,
2263 dst, size);
2265 } else {
2266 sin = (struct sockaddr_in *)src;
2267 result = inet_ntop(af, &sin->sin_addr, dst, size);
2270 return (result);
2274 secure_command(char *cmd)
2276 unsigned char *in = NULL, *out = NULL;
2277 int length = 0;
2278 size_t inlen;
2280 if ((auth_type != AUTHTYPE_NONE) && clevel != PROT_C) {
2281 gss_buffer_desc in_buf, out_buf;
2282 OM_uint32 maj_stat, min_stat;
2284 /* secure_command (based on level) */
2285 if (auth_type == AUTHTYPE_GSSAPI) {
2286 OM_uint32 expire_time;
2287 int conf_state;
2288 /* clevel = PROT_P; */
2289 in_buf.value = cmd;
2290 in_buf.length = strlen(cmd) + 1;
2292 maj_stat = gss_context_time(&min_stat, gcontext,
2293 &expire_time);
2294 if (GSS_ERROR(maj_stat)) {
2295 user_gss_error(maj_stat, min_stat,
2296 "gss context has expired");
2297 fatal("Your gss credentials have expired. "
2298 "Good-bye!");
2300 maj_stat = gss_seal(&min_stat, gcontext,
2301 (clevel == PROT_P), /* private */
2302 GSS_C_QOP_DEFAULT,
2303 &in_buf, &conf_state,
2304 &out_buf);
2305 if (maj_stat != GSS_S_COMPLETE) {
2306 /* generally need to deal */
2307 user_gss_error(maj_stat, min_stat,
2308 (clevel == PROT_P) ?
2309 "gss_seal ENC didn't complete":
2310 "gss_seal MIC didn't complete");
2311 } else if ((clevel == PROT_P) && !conf_state) {
2312 (void) fprintf(stderr,
2313 "GSSAPI didn't encrypt message");
2314 out = out_buf.value;
2315 } else {
2316 if (debug)
2317 (void) fprintf(stderr,
2318 "sealed (%s) %d bytes\n",
2319 clevel == PROT_P ? "ENC" : "MIC",
2320 out_buf.length);
2322 out = out_buf.value;
2325 /* Other auth types go here ... */
2326 inlen = ((4 * out_buf.length) / 3) + 4;
2327 in = (uchar_t *)malloc(inlen);
2328 if (in == NULL) {
2329 gss_release_buffer(&min_stat, &out_buf);
2330 fatal("Memory error allocating space for response.");
2332 length = out_buf.length;
2333 if (auth_error = radix_encode(out, in, inlen, &length, 0)) {
2334 (void) fprintf(stderr,
2335 "Couldn't base 64 encode command (%s)\n",
2336 radix_error(auth_error));
2337 free(in);
2338 gss_release_buffer(&min_stat, &out_buf);
2339 return (0);
2342 (void) fprintf(ctrl_out, "%s %s",
2343 clevel == PROT_P ? "ENC" : "MIC", in);
2345 free(in);
2346 gss_release_buffer(&min_stat, &out_buf);
2348 if (debug)
2349 (void) fprintf(stderr,
2350 "secure_command(%s)\nencoding %d bytes %s %s\n",
2351 cmd, length,
2352 (clevel == PROT_P) ? "ENC" : "MIC", in);
2353 } else {
2355 * auth_type = AUTHTYPE_NONE or
2356 * command channel is not protected
2358 fputs(cmd, ctrl_out);
2361 (void) fprintf(ctrl_out, "\r\n");
2362 (void) fflush(ctrl_out);
2363 return (1);
2366 unsigned int maxbuf;
2367 unsigned char *ucbuf;
2369 void
2370 setpbsz(unsigned int size)
2372 unsigned int actualbuf;
2373 int oldverbose;
2375 if (ucbuf)
2376 (void) free(ucbuf);
2377 actualbuf = size;
2378 while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL) {
2379 if (actualbuf)
2380 actualbuf >>= 2;
2381 else {
2382 perror("Error while trying to malloc PROT buffer:");
2383 exit(1);
2386 oldverbose = verbose;
2387 verbose = 0;
2388 reply_parse = "PBSZ=";
2389 if (command("PBSZ %u", actualbuf) != COMPLETE)
2390 fatal("Cannot set PROT buffer size");
2391 if (reply_parse) {
2392 if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
2393 maxbuf = actualbuf;
2394 } else
2395 maxbuf = actualbuf;
2396 reply_parse = NULL;
2397 verbose = oldverbose;
2401 * Do the base 64 decoding of the raw input buffer, b64_buf.
2402 * Also do the verification and decryption, if required.
2403 * retval contains the current error code number
2405 * returns:
2406 * (RFC 2228: error returns are 3 digit numbers of the form 5xy)
2407 * 5 if an error occurred
2409 static int
2410 decode_reply(uchar_t *plain_buf,
2411 int ilen,
2412 uchar_t *b64_buf,
2413 int retval,
2414 boolean_t *again)
2416 int len;
2417 int safe = 0;
2419 *again = 0;
2421 if (!b64_buf[0]) /* if there is no string, no problem */
2422 return (retval);
2424 if ((auth_type == AUTHTYPE_NONE)) {
2425 (void) printf("Cannot decode reply:\n%d %s\n", code, b64_buf);
2426 return ('5');
2429 switch (code) {
2431 case 631: /* 'safe' */
2432 safe = 1;
2433 break;
2435 case 632: /* 'private' */
2436 break;
2438 case 633: /* 'confidential' */
2439 break;
2441 default:
2442 (void) printf("Unknown reply: %d %s\n", code, b64_buf);
2443 return ('5');
2446 /* decode the base64 encoded message */
2447 auth_error = radix_encode(b64_buf, plain_buf, ilen, &len, 1);
2449 if (auth_error) {
2450 (void) printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
2451 code, radix_error(auth_error), b64_buf);
2452 return ('5');
2455 if (auth_type == AUTHTYPE_GSSAPI) {
2456 gss_buffer_desc xmit_buf, msg_buf;
2457 OM_uint32 maj_stat, min_stat;
2458 int conf_state = safe;
2459 xmit_buf.value = plain_buf;
2460 xmit_buf.length = len;
2462 /* decrypt/verify the message */
2463 maj_stat = gss_unseal(&min_stat, gcontext,
2464 &xmit_buf, &msg_buf, &conf_state, NULL);
2465 if (maj_stat != GSS_S_COMPLETE) {
2466 user_gss_error(maj_stat, min_stat,
2467 "failed unsealing reply");
2468 return ('5');
2470 if (msg_buf.length < ilen - 2 - 1) {
2471 memcpy(plain_buf, msg_buf.value, msg_buf.length);
2472 strcpy((char *)&plain_buf[msg_buf.length], "\r\n");
2473 gss_release_buffer(&min_stat, &msg_buf);
2474 *again = 1;
2475 } else {
2476 user_gss_error(maj_stat, min_stat,
2477 "reply was too long");
2478 return ('5');
2480 } /* end if GSSAPI */
2482 /* Other auth types go here... */
2484 return (retval);