8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / ftp / ftp.c
blobf92dcc6759f03366b0a5abf020faf588cb001638
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 #pragma ident "%Z%%M% %I% %E% SMI"
42 #include "ftp_var.h"
43 #include <arpa/nameser.h>
44 #include <sys/types.h>
47 * WRITE() returns:
48 * >0 no error
49 * -1 error, errorno is set
50 * -2 security error (secure_write() only)
52 #define PUTC(x, y) secure_putc(x, y)
53 #define READ(x, y, z) secure_read(x, y, z)
54 #define WRITE(x, y, z) secure_write(x, y, z)
56 static struct sockaddr_in6 data_addr;
57 int data = -1;
58 static int abrtflag = 0;
59 static int ptflag = 0;
60 int connected;
61 static jmp_buf sendabort;
62 static jmp_buf recvabort;
63 static jmp_buf ptabort;
64 static int ptabflg;
65 static boolean_t pasv_refused;
66 boolean_t eport_supported = B_TRUE;
68 * For IPv6 addresses, EPSV will be the default (rather than EPRT/LPRT).
69 * The EPSV/ERPT ftp protocols are specified in RFC 2428.
71 * Perform EPSV if passivemode is set and ipv6rem is TRUE.
73 static boolean_t ipv6rem;
74 int use_eprt = 0; /* Testing option that specifies EPRT by default */
75 FILE *ctrl_in, *ctrl_out;
77 static void abortsend(int sig);
78 static void abortpt(int sig);
79 static void proxtrans(char *cmd, char *local, char *remote);
80 static void cmdabort(int sig);
81 static int empty(struct fd_set *mask, int sec, int nfds);
82 static void abortrecv(int sig);
83 static int initconn(void);
84 static FILE *dataconn(char *mode);
85 static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
86 hrtime_t t1, char *local, char *remote);
87 static void psabort(int sig);
88 static char *gunique(char *local);
89 static const char *inet_ntop_native(int af, const void *src, char *dst,
90 size_t size);
91 static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
93 static int secure_command(char *);
94 static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
96 static ssize_t bufcnt; /* number of bytes in buf[] */
97 static char *bufp; /* next character in buf */
98 static int buferr; /* last errno */
99 static size_t bufsize;
101 static void fdio_setbuf(char *buffer, size_t bufsize);
102 static int fdio_fillbuf(int fd);
103 static int fdio_error(int fd);
104 #define fdio_getc(fd) (--bufcnt < 0 ? fdio_fillbuf((fd)) : \
105 ((unsigned char)*bufp++))
107 #define MAX(a, b) ((a) > (b) ? (a) : (b))
108 #define NONZERO(x) ((x) == 0 ? 1 : (x))
110 static void
111 fdio_setbuf(char *buffer, size_t maxsize)
113 buf = buffer;
114 bufp = buf;
115 bufcnt = 0;
116 buferr = 0;
117 bufsize = maxsize;
120 static int
121 fdio_fillbuf(int fd)
123 bufcnt = timedread(fd, buf, bufsize, timeout);
124 if (bufcnt < 0)
125 buferr = errno;
126 if (bufcnt <= 0)
127 return (EOF);
128 bufp = buf;
129 bufcnt--;
130 return ((unsigned char)*bufp++);
134 * fdio_error - used on a file descriptor instead of ferror()
137 /*ARGSUSED*/
138 static int
139 fdio_error(int fd)
141 return (buferr);
145 * timedread - read buffer (like "read"), but with timeout (in seconds)
148 static ssize_t
149 timedread(int fd, void *buf, size_t size, int timeout)
151 struct fd_set mask;
152 struct timeval tv;
153 int err;
155 if (!timeout)
156 return (READ(fd, buf, size));
158 tv.tv_sec = (time_t)timeout;
159 tv.tv_usec = 0;
161 FD_ZERO(&mask);
162 FD_SET(fd, &mask);
164 err = select(fd + 1, &mask, NULL, NULL, &tv);
165 if (err == 0)
166 errno = ETIMEDOUT;
167 if (err <= 0)
168 return (-1);
170 return (READ(fd, buf, size));
174 char *
175 hookup(char *host, char *service)
177 struct addrinfo hints, *ai = NULL, *ai_head;
178 int s;
179 socklen_t len;
180 static char hostnamebuf[80];
181 struct in6_addr ipv6addr;
182 char abuf[INET6_ADDRSTRLEN];
183 int error_num;
184 int on = 1;
187 * There appears to be a bug in getaddrinfo() where, if the
188 * ai_family is set to AF_INET6, and the host is a v4-only
189 * host, getaddrinfo() returns an error instead of returning
190 * an v4-mapped ipv6 address. Therefore the ai_family is
191 * set to AF_UNSPEC and any returned v4 addresses are
192 * explicitly mapped within ftp.
194 bzero((char *)&remctladdr, sizeof (remctladdr));
195 bzero((char *)&hints, sizeof (hints));
196 hints.ai_flags = AI_CANONNAME;
197 hints.ai_family = AF_UNSPEC;
198 hints.ai_socktype = SOCK_STREAM;
200 error_num = getaddrinfo(host, service, &hints, &ai);
201 if (error_num != 0) {
202 if (error_num == EAI_AGAIN) {
203 (void) printf(
204 "%s: unknown host or invalid literal address "
205 "(try again later)\n", host);
206 } else {
207 (void) printf(
208 "%s: unknown host or invalid literal address\n",
209 host);
211 code = -1;
212 return ((char *)0);
214 ai_head = ai;
218 * If ai_canonname is a IPv4-mapped IPv6 literal, we'll convert it to
219 * IPv4 literal address.
221 if (ai->ai_canonname != NULL &&
222 (inet_pton(AF_INET6, ai->ai_canonname, &ipv6addr) > 0) &&
223 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
224 struct in_addr src4;
225 hostnamebuf[0] = '\0';
226 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &src4);
227 (void) inet_ntop(AF_INET, &src4, hostnamebuf,
228 sizeof (hostnamebuf));
231 * It can even be the case that the "host" supplied by the user
232 * can be a IPv4-mapped IPv6 literal. So, let's fix that too.
234 if ((inet_pton(AF_INET6, host, &ipv6addr) > 0) &&
235 IN6_IS_ADDR_V4MAPPED(&ipv6addr) &&
236 strlen(hostnamebuf) <= strlen(host)) {
237 (void) strlcpy(host, hostnamebuf, strlen(host) + 1);
239 } else {
240 reset_timer();
241 (void) strlcpy(hostnamebuf,
242 (ai->ai_canonname ? ai->ai_canonname : host),
243 sizeof (hostnamebuf));
246 hostname = hostnamebuf;
247 for (;;) {
248 int oerrno;
250 bcopy(ai->ai_addr, &remctladdr, ai->ai_addrlen);
251 if (ai->ai_addr->sa_family == AF_INET) {
252 IN6_INADDR_TO_V4MAPPED(
253 &(((struct sockaddr_in *)ai->ai_addr)->sin_addr),
254 &remctladdr.sin6_addr);
255 remctladdr.sin6_family = AF_INET6;
258 s = socket(AF_INET6, SOCK_STREAM, 0);
259 if (s < 0) {
260 perror("ftp: socket");
261 code = -1;
262 freeaddrinfo(ai_head);
263 return (0);
265 if (timeout && setsockopt(s, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
266 (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
267 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
268 reset_timer();
270 error_num = connect(s, (struct sockaddr *)&remctladdr,
271 sizeof (remctladdr));
272 oerrno = errno;
273 if (error_num >= 0)
274 break;
277 * Maintain message behavior: only include the address in
278 * our error message if we have another one to try; if this
279 * is the last address on our list, just print the error.
281 if (ai->ai_next != NULL) {
282 (void) fprintf(stderr, "ftp: connect to address %s: ",
283 inet_ntop_native(ai->ai_addr->sa_family,
284 (void *)ai->ai_addr, abuf, sizeof (abuf)));
285 errno = oerrno;
286 perror((char *)0);
287 } else {
288 perror("ftp: connect");
289 code = -1;
290 freeaddrinfo(ai_head);
291 goto bad;
293 ai = ai->ai_next;
294 (void) fprintf(stdout, "Trying %s...\n",
295 inet_ntop_native(ai->ai_addr->sa_family,
296 (void *)ai->ai_addr, abuf, sizeof (abuf)));
297 (void) close(s);
301 /* Set ipv6rem to TRUE if control connection is a native IPv6 address */
302 if (IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr))
303 ipv6rem = B_FALSE;
304 else
305 ipv6rem = B_TRUE;
308 freeaddrinfo(ai_head);
309 ai = NULL;
312 * Set passive mode flag on by default only if a native IPv6 address
313 * is being used -and- the use_eprt is not set.
315 if (ipv6rem == B_TRUE && use_eprt == 0)
316 passivemode = 1;
318 len = sizeof (myctladdr);
319 if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
320 perror("ftp: getsockname");
321 code = -1;
322 goto bad;
324 ctrl_in = fdopen(s, "r");
325 ctrl_out = fdopen(s, "w");
326 if (ctrl_in == NULL || ctrl_out == NULL) {
327 (void) fprintf(stderr, "ftp: fdopen failed.\n");
328 if (ctrl_in)
329 (void) fclose(ctrl_in);
330 if (ctrl_out)
331 (void) fclose(ctrl_out);
332 code = -1;
333 goto bad;
335 if (verbose)
336 (void) printf("Connected to %s.\n", hostname);
337 if (getreply(0) > 2) { /* read startup message from server */
338 if (ctrl_in)
339 (void) fclose(ctrl_in);
340 if (ctrl_out)
341 (void) fclose(ctrl_out);
342 ctrl_in = ctrl_out = NULL;
343 ctrl_in = ctrl_out = NULL;
344 code = -1;
345 goto bad;
347 if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
348 sizeof (on)) < 0 && debug)
349 perror("ftp: setsockopt (SO_OOBINLINE)");
351 return (hostname);
352 bad:
353 (void) close(s);
354 return ((char *)0);
358 login(char *host)
360 char tmp[80];
361 char *user, *pass, *acct;
362 int n, aflag = 0;
364 user = pass = acct = 0;
365 if (ruserpass(host, &user, &pass, &acct) < 0) {
366 disconnect(0, NULL);
367 code = -1;
368 return (0);
370 if (user == NULL) {
371 char *myname = getlogin();
373 if (myname == NULL) {
374 struct passwd *pp = getpwuid(getuid());
376 if (pp != NULL)
377 myname = pp->pw_name;
379 stop_timer();
380 (void) printf("Name (%s:%s): ", host,
381 (myname == NULL) ? "" : myname);
382 *tmp = '\0';
383 if (fgets(tmp, sizeof (tmp) - 1, stdin) != NULL)
384 tmp[strlen(tmp) - 1] = '\0';
385 if (*tmp != '\0')
386 user = tmp;
387 else if (myname != NULL)
388 user = myname;
389 else
390 return (0);
392 n = command("USER %s", user);
393 if (n == CONTINUE) {
394 int oldclevel;
395 if (pass == NULL)
396 pass = mygetpass("Password:");
397 oldclevel = clevel;
398 clevel = PROT_P;
399 n = command("PASS %s", pass);
400 /* level may have changed */
401 if (clevel == PROT_P)
402 clevel = oldclevel;
404 if (n == CONTINUE) {
405 aflag++;
406 if (acct == NULL)
407 acct = mygetpass("Account:");
408 n = command("ACCT %s", acct);
410 if (n != COMPLETE) {
411 (void) fprintf(stderr, "Login failed.\n");
412 return (0);
414 if (!aflag && acct != NULL)
415 (void) command("ACCT %s", acct);
416 if (proxy)
417 return (1);
418 for (n = 0; n < macnum; ++n) {
419 if (strcmp("init", macros[n].mac_name) == 0) {
420 (void) strlcpy(line, "$init", sizeof (line));
421 makeargv();
422 domacro(margc, margv);
423 break;
426 return (1);
429 /*ARGSUSED*/
430 static void
431 cmdabort(int sig)
433 (void) printf("\n");
434 (void) fflush(stdout);
435 abrtflag++;
436 if (ptflag)
437 longjmp(ptabort, 1);
441 command(char *fmt, ...)
443 int r;
444 void (*oldintr)();
445 va_list ap;
446 char command_buf[FTPBUFSIZ];
448 va_start(ap, fmt);
449 abrtflag = 0;
450 if (debug) {
451 (void) printf("---> ");
452 if (strncmp("PASS ", fmt, 5) == 0)
453 (void) printf("PASS XXXX");
454 else if (strncmp("ACCT ", fmt, 5) == 0)
455 (void) printf("ACCT XXXX");
456 else
457 (void) vfprintf(stdout, fmt, ap);
458 (void) printf("\n");
459 (void) fflush(stdout);
461 if (ctrl_out == NULL) {
462 perror("No control connection for command");
463 code = -1;
464 return (0);
466 oldintr = signal(SIGINT, cmdabort);
467 (void) vsnprintf(command_buf, FTPBUFSIZ, fmt, ap);
468 va_end(ap);
470 again: if (secure_command(command_buf) == 0)
471 return (0);
473 cpend = 1;
474 r = getreply(strcmp(fmt, "QUIT") == 0);
476 if (r == 533 && clevel == PROT_P) {
477 (void) fprintf(stderr, "ENC command not supported at server; "
478 "retrying under MIC...\n");
479 clevel = PROT_S;
480 goto again;
483 if (abrtflag && oldintr != SIG_IGN)
484 (*oldintr)();
485 (void) signal(SIGINT, oldintr);
486 return (r);
489 /* Need to save reply reponse from server for use in EPSV mode */
490 char reply_string[BUFSIZ];
493 getreply(int expecteof)
496 * 'code' is the 3 digit reply code, form xyz
497 * 'dig' counts the number of digits we are along in the code
498 * 'n' is the first digit of 'code'
499 * 4yz: resource unavailable
500 * 5yz: an error occurred, failure
501 * 6yz: protected reply (is_base64 == TRUE)
502 * 631 - base 64 encoded safe message
503 * 632 - base 64 encoded private message
504 * 633 - base 64 encoded confidential message
505 * 'c' is a wide char type, for international char sets
507 wint_t c;
508 int i, n;
509 int dig;
510 int originalcode = 0, continuation = 0;
511 void (*oldintr)();
512 int pflag = 0;
513 char *pt = pasv;
515 * this is the input and output buffers needed for
516 * radix_encode()
518 unsigned char ibuf[FTPBUFSIZ];
519 unsigned char obuf[FTPBUFSIZ];
520 boolean_t is_base64;
521 int len;
522 char *cp;
524 if (!ctrl_in)
525 return (0);
526 oldintr = signal(SIGINT, cmdabort);
528 ibuf[0] = '\0';
530 if (reply_parse)
531 reply_ptr = reply_buf;
533 for (;;) {
534 obuf[0] = '\0';
535 dig = n = code = 0;
536 i = is_base64 = 0;
537 cp = reply_string;
538 reset_timer(); /* once per line */
540 while ((c = ibuf[0] ?
541 (wint_t)ibuf[i++] : fgetwc(ctrl_in)) != '\n') {
543 if (i >= FTPBUFSIZ)
544 break;
546 if (c == IAC) { /* handle telnet commands */
547 switch (c = fgetwc(ctrl_in)) {
548 case WILL:
549 case WONT:
550 c = fgetwc(ctrl_in);
551 (void) fprintf(ctrl_out, "%c%c%wc", IAC,
552 WONT, c);
553 (void) fflush(ctrl_out);
554 break;
555 case DO:
556 case DONT:
557 c = fgetwc(ctrl_in);
558 (void) fprintf(ctrl_out, "%c%c%wc", IAC,
559 DONT, c);
560 (void) fflush(ctrl_out);
561 break;
562 default:
563 break;
565 continue;
567 dig++;
568 if (c == EOF) {
569 if (expecteof) {
570 (void) signal(SIGINT, oldintr);
571 code = 221;
572 return (0);
574 lostpeer(0);
575 if (verbose) {
576 (void) printf(
577 "421 Service not available, remote"
578 " server has closed connection\n");
579 } else
580 (void) printf("Lost connection\n");
581 (void) fflush(stdout);
582 code = 421;
583 return (4);
585 if (n == 0)
586 n = c;
588 if (n == '6')
589 is_base64 = 1;
591 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
592 (is_base64 || continuation)) {
593 /* start storing chars in obuf */
594 if (c != '\r' && dig > 4)
595 obuf[i++] = (char)c;
596 } else {
597 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
598 dig == 1 && verbose)
599 (void) printf("Unauthenticated reply received "
600 "from server:\n");
601 if (reply_parse)
602 *reply_ptr++ = (char)c;
603 if (c != '\r' && (verbose > 0 ||
604 (verbose > -1 && n == '5' && dig > 4))) {
605 if (proxflag &&
606 (dig == 1 || dig == 5 && verbose == 0))
607 (void) printf("%s:", hostname);
608 (void) putwchar(c);
610 } /* endif auth_type && !ibuf[0] ... */
612 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
613 continue;
615 /* we are still extracting the 3 digit code */
616 if (dig < 4 && isascii(c) && isdigit(c))
617 code = code * 10 + (c - '0');
619 /* starting passive mode */
620 if (!pflag && code == 227)
621 pflag = 1;
623 /* start to store characters, when dig > 4 */
624 if (dig > 4 && pflag == 1 && isascii(c) && isdigit(c))
625 pflag = 2;
626 if (pflag == 2) {
627 if (c != '\r' && c != ')') {
628 /* the mb array is to deal with the wchar_t */
629 char mb[MB_LEN_MAX];
630 int avail;
633 * space available in pasv[], accounting
634 * for trailing NULL
636 avail = &pasv[sizeof (pasv)] - pt - 1;
638 len = wctomb(mb, c);
639 if (len <= 0 && avail > 0) {
640 *pt++ = (unsigned char)c;
641 } else if (len > 0 && avail >= len) {
642 bcopy(mb, pt, (size_t)len);
643 pt += len;
644 } else {
646 * no room in pasv[];
647 * close connection
649 (void) printf("\nReply too long - "
650 "closing connection\n");
651 lostpeer(0);
652 (void) fflush(stdout);
653 (void) signal(SIGINT, oldintr);
654 return (4);
656 } else {
657 *pt = '\0';
658 pflag = 3;
660 } /* endif pflag == 2 */
661 if (dig == 4 && c == '-' && !is_base64) {
662 if (continuation)
663 code = 0;
664 continuation++;
666 if (cp < &reply_string[sizeof (reply_string) - 1])
667 *cp++ = c;
669 } /* end while */
671 if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
672 return (getreply(expecteof));
674 ibuf[0] = obuf[i] = '\0';
676 if (code && is_base64) {
677 boolean_t again = 0;
678 n = decode_reply(ibuf, sizeof (ibuf), obuf, n, &again);
679 if (again)
680 continue;
681 } else
683 if (verbose > 0 || verbose > -1 && n == '5') {
684 (void) putwchar(c);
685 (void) fflush(stdout);
688 if (continuation && code != originalcode) {
689 ibuf[0] = obuf[i] = '\0';
690 if (originalcode == 0)
691 originalcode = code;
692 continue;
694 *cp = '\0';
695 if (n != '1')
696 cpend = 0;
697 (void) signal(SIGINT, oldintr);
698 if (code == 421 || originalcode == 421)
699 lostpeer(0);
700 if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
701 (*oldintr)();
703 if (reply_parse) {
704 *reply_ptr = '\0';
705 if (reply_ptr = strstr(reply_buf, reply_parse)) {
706 reply_parse = reply_ptr + strlen(reply_parse);
707 if (reply_ptr = strpbrk(reply_parse, " \r"))
708 *reply_ptr = '\0';
709 } else
710 reply_parse = reply_ptr;
713 return (n - '0');
714 } /* end for */
717 static int
718 empty(struct fd_set *mask, int sec, int nfds)
720 struct timeval t;
722 reset_timer();
723 t.tv_sec = (time_t)sec;
724 t.tv_usec = 0;
725 return (select(nfds, mask, NULL, NULL, &t));
728 /*ARGSUSED*/
729 static void
730 abortsend(int sig)
732 mflag = 0;
733 abrtflag = 0;
734 (void) printf("\nsend aborted\n");
735 (void) fflush(stdout);
736 longjmp(sendabort, 1);
739 void
740 sendrequest(char *cmd, char *local, char *remote, int allowpipe)
742 FILE *fin, *dout = 0;
743 int (*closefunc)();
744 void (*oldintr)(), (*oldintp)();
745 off_t bytes = 0, hashbytes = HASHSIZ;
746 int c;
748 * d >= 0 if there is no error
749 * -1 if there was a normal file i/o error
750 * -2 if there was a security error
752 int d;
753 struct stat st;
754 hrtime_t start, stop;
755 char *dmode;
757 if (proxy) {
758 proxtrans(cmd, local, remote);
759 return;
761 closefunc = NULL;
762 oldintr = NULL;
763 oldintp = NULL;
764 dmode = "w";
765 if (setjmp(sendabort)) {
766 while (cpend) {
767 (void) getreply(0);
769 if (data >= 0) {
770 (void) close(data);
771 data = -1;
773 if (oldintr)
774 (void) signal(SIGINT, oldintr);
775 if (oldintp)
776 (void) signal(SIGPIPE, oldintp);
777 code = -1;
778 restart_point = 0;
779 return;
781 oldintr = signal(SIGINT, abortsend);
782 if (strcmp(local, "-") == 0)
783 fin = stdin;
784 else if (allowpipe && *local == '|') {
785 oldintp = signal(SIGPIPE, SIG_IGN);
786 fin = mypopen(local + 1, "r");
787 if (fin == NULL) {
788 perror(local + 1);
789 (void) signal(SIGINT, oldintr);
790 (void) signal(SIGPIPE, oldintp);
791 code = -1;
792 restart_point = 0;
793 return;
795 closefunc = mypclose;
796 } else {
797 fin = fopen(local, "r");
798 if (fin == NULL) {
799 perror(local);
800 (void) signal(SIGINT, oldintr);
801 code = -1;
802 restart_point = 0;
803 return;
805 closefunc = fclose;
806 if (fstat(fileno(fin), &st) < 0 ||
807 (st.st_mode&S_IFMT) != S_IFREG) {
808 (void) fprintf(stdout,
809 "%s: not a plain file.\n", local);
810 (void) signal(SIGINT, oldintr);
811 code = -1;
812 (void) fclose(fin);
813 restart_point = 0;
814 return;
817 if (initconn()) {
818 (void) signal(SIGINT, oldintr);
819 if (oldintp)
820 (void) signal(SIGPIPE, oldintp);
821 code = -1;
822 if (closefunc != NULL)
823 (*closefunc)(fin);
824 restart_point = 0;
825 return;
827 if (setjmp(sendabort))
828 goto abort;
829 if ((restart_point > 0) &&
830 (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
831 if (fseeko(fin, restart_point, SEEK_SET) < 0) {
832 perror(local);
833 if (closefunc != NULL)
834 (*closefunc)(fin);
835 restart_point = 0;
836 return;
838 if (command("REST %lld", (longlong_t)restart_point)
839 != CONTINUE) {
840 if (closefunc != NULL)
841 (*closefunc)(fin);
842 restart_point = 0;
843 return;
845 dmode = "r+w";
847 restart_point = 0;
848 if (remote) {
849 if (command("%s %s", cmd, remote) != PRELIM) {
850 (void) signal(SIGINT, oldintr);
851 if (oldintp)
852 (void) signal(SIGPIPE, oldintp);
853 if (closefunc != NULL)
854 (*closefunc)(fin);
855 if (data >= 0) {
856 (void) close(data);
857 data = -1;
859 return;
861 } else
862 if (command("%s", cmd) != PRELIM) {
863 (void) signal(SIGINT, oldintr);
864 if (oldintp)
865 (void) signal(SIGPIPE, oldintp);
866 if (closefunc != NULL)
867 (*closefunc)(fin);
868 if (data >= 0) {
869 (void) close(data);
870 data = -1;
872 return;
874 dout = dataconn(dmode);
875 if (dout == NULL)
876 goto abort;
877 stop_timer();
878 oldintp = signal(SIGPIPE, SIG_IGN);
879 start = gethrtime();
880 switch (type) {
882 case TYPE_I:
883 case TYPE_L:
884 errno = d = 0;
885 while ((c = read(fileno(fin), buf, FTPBUFSIZ)) > 0) {
886 if ((d = WRITE(fileno(dout), buf, c)) < 0)
887 break;
888 bytes += c;
889 if (hash) {
890 while (bytes >= hashbytes) {
891 (void) putchar('#');
892 hashbytes += HASHSIZ;
894 (void) fflush(stdout);
897 if (hash && bytes > 0) {
898 if (bytes < hashbytes)
899 (void) putchar('#');
900 (void) putchar('\n');
901 (void) fflush(stdout);
903 if (c < 0)
904 perror(local);
906 if (d >= 0)
907 d = secure_flush(fileno(dout));
909 if (d < 0) {
910 if ((d == -1) && (errno != EPIPE))
911 perror("netout");
912 bytes = -1;
914 break;
916 case TYPE_A:
917 while ((c = getc(fin)) != EOF) {
918 if (c == '\n') {
919 while (hash && (bytes >= hashbytes)) {
920 (void) putchar('#');
921 (void) fflush(stdout);
922 hashbytes += HASHSIZ;
924 if (ferror(dout) || PUTC('\r', dout) < 0)
925 break;
926 bytes++;
929 if (PUTC(c, dout) < 0)
930 break;
931 bytes++;
932 #ifdef notdef
933 if (c == '\r') {
934 /* this violates rfc */
935 (void) PUTC('\0', dout);
936 bytes++;
938 #endif
940 if (hash && bytes > 0) {
941 if (bytes < hashbytes)
942 (void) putchar('#');
943 (void) putchar('\n');
944 (void) fflush(stdout);
946 if (ferror(fin))
947 perror(local);
949 d = ferror(dout) ? -1 : 0;
950 if (d == 0)
951 d = secure_flush(fileno(dout));
953 if (d < 0) {
954 if ((d == -1) && (errno != EPIPE))
955 perror("netout");
956 bytes = -1;
958 break;
960 reset_timer();
961 if (closefunc != NULL)
962 (*closefunc)(fin);
963 if (ctrl_in != NULL) {
964 int dfn = fileno(dout);
965 int nfds = fileno(ctrl_in);
966 fd_set mask;
969 * There could be data not yet written to dout,
970 * in the stdio buffer; so, before a shutdown()
971 * on further sends, do fflush(dout)
973 (void) fflush(dout);
975 /* sending over; shutdown sending on dfn */
976 (void) shutdown(dfn, SHUT_WR);
977 FD_ZERO(&mask);
978 FD_SET(dfn, &mask);
979 FD_SET(nfds, &mask);
980 nfds = MAX(dfn, nfds);
983 * Wait for remote end to either close data socket
984 * or ack that we've closed our end; it doesn't
985 * matter which happens first.
987 (void) select(nfds + 1, &mask, NULL, NULL, NULL);
989 (void) fclose(dout); data = -1;
990 stop = gethrtime();
991 (void) getreply(0);
992 (void) signal(SIGINT, oldintr);
993 if (oldintp)
994 (void) signal(SIGPIPE, oldintp);
997 * Only print the transfer successful message if the code returned
998 * from remote is 226 or 250. All other codes are error codes.
1000 if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
1001 ptransfer("sent", bytes, start, stop, local, remote);
1002 if (!ctrl_in)
1003 (void) printf("Lost connection\n");
1004 return;
1005 abort:
1006 (void) signal(SIGINT, oldintr);
1007 if (oldintp)
1008 (void) signal(SIGPIPE, oldintp);
1009 if (!cpend) {
1010 code = -1;
1011 return;
1013 if (data >= 0) {
1014 (void) close(data);
1015 data = -1;
1017 if (dout) {
1018 (void) fclose(dout);
1019 data = -1;
1021 (void) getreply(0);
1022 code = -1;
1023 if (closefunc != NULL && fin != NULL)
1024 (*closefunc)(fin);
1025 stop = gethrtime();
1027 * Only print the transfer successful message if the code returned
1028 * from remote is 226 or 250. All other codes are error codes.
1030 if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
1031 ptransfer("sent", bytes, start, stop, local, remote);
1032 if (!ctrl_in)
1033 (void) printf("Lost connection\n");
1034 restart_point = 0;
1037 /*ARGSUSED*/
1038 static void
1039 abortrecv(int sig)
1041 mflag = 0;
1042 abrtflag = 0;
1043 (void) printf("\n");
1044 (void) fflush(stdout);
1045 longjmp(recvabort, 1);
1048 void
1049 recvrequest(char *cmd, char *local, char *remote, char *mode, int allowpipe)
1051 FILE *fout, *din = 0;
1052 int (*closefunc)();
1053 void (*oldintr)(), (*oldintp)();
1054 int oldverbose, oldtype = 0, tcrflag, nfnd;
1055 char msg;
1056 off_t bytes = 0, hashbytes = HASHSIZ;
1057 struct fd_set mask;
1058 int c, d, n;
1059 hrtime_t start, stop;
1060 int errflg = 0;
1061 int infd;
1062 int nfds;
1063 int retrcmd;
1065 retrcmd = (strcmp(cmd, "RETR") == 0);
1066 if (proxy && retrcmd) {
1067 proxtrans(cmd, local, remote);
1068 return;
1070 closefunc = NULL;
1071 oldintr = NULL;
1072 oldintp = NULL;
1073 tcrflag = !crflag && retrcmd;
1074 if (setjmp(recvabort)) {
1075 while (cpend) {
1076 (void) getreply(0);
1078 if (data >= 0) {
1079 (void) close(data);
1080 data = -1;
1082 if (oldintr)
1083 (void) signal(SIGINT, oldintr);
1084 code = -1;
1085 return;
1087 oldintr = signal(SIGINT, abortrecv);
1088 if (local != NULL &&
1089 strcmp(local, "-") != 0 &&
1090 (*local != '|' || !allowpipe)) {
1091 if (access(local, W_OK) < 0) {
1092 char *dir = rindex(local, '/');
1093 int file_errno = errno;
1095 if (file_errno != ENOENT && file_errno != EACCES) {
1096 perror(local);
1097 (void) signal(SIGINT, oldintr);
1098 code = -1;
1099 return;
1101 if ((dir != NULL) && (dir != local))
1102 *dir = 0;
1103 if (dir == local)
1104 d = access("/", W_OK);
1105 else
1106 d = access(dir ? local : ".", W_OK);
1107 if ((dir != NULL) && (dir != local))
1108 *dir = '/';
1109 if (d < 0) {
1110 perror(local);
1111 (void) signal(SIGINT, oldintr);
1112 code = -1;
1113 return;
1115 if (!runique && file_errno == EACCES) {
1116 errno = file_errno;
1117 perror(local);
1118 (void) signal(SIGINT, oldintr);
1119 code = -1;
1120 return;
1122 if (runique && file_errno == EACCES &&
1123 (local = gunique(local)) == NULL) {
1124 (void) signal(SIGINT, oldintr);
1125 code = -1;
1126 return;
1128 } else if (runique && (local = gunique(local)) == NULL) {
1129 (void) signal(SIGINT, oldintr);
1130 code = -1;
1131 return;
1134 if (initconn()) {
1135 (void) signal(SIGINT, oldintr);
1136 code = -1;
1137 return;
1139 if (setjmp(recvabort))
1140 goto abort;
1141 if (!retrcmd && type != TYPE_A) {
1142 oldtype = type;
1143 oldverbose = verbose;
1144 if (!debug)
1145 verbose = 0;
1146 setascii(0, NULL);
1147 verbose = oldverbose;
1149 if ((restart_point > 0) && retrcmd &&
1150 command("REST %lld", (longlong_t)restart_point) != CONTINUE) {
1151 return;
1153 if (remote) {
1154 if (command("%s %s", cmd, remote) != PRELIM) {
1155 (void) signal(SIGINT, oldintr);
1156 if (oldtype) {
1157 if (!debug)
1158 verbose = 0;
1159 switch (oldtype) {
1160 case TYPE_I:
1161 setbinary(0, NULL);
1162 break;
1163 case TYPE_E:
1164 setebcdic(0, NULL);
1165 break;
1166 case TYPE_L:
1167 settenex(0, NULL);
1168 break;
1170 verbose = oldverbose;
1172 return;
1174 } else {
1175 if (command("%s", cmd) != PRELIM) {
1176 (void) signal(SIGINT, oldintr);
1177 if (oldtype) {
1178 if (!debug)
1179 verbose = 0;
1180 switch (oldtype) {
1181 case TYPE_I:
1182 setbinary(0, NULL);
1183 break;
1184 case TYPE_E:
1185 setebcdic(0, NULL);
1186 break;
1187 case TYPE_L:
1188 settenex(0, NULL);
1189 break;
1191 verbose = oldverbose;
1193 return;
1196 din = dataconn("r");
1197 if (din == NULL)
1198 goto abort;
1200 if (local == NULL) {
1201 fout = tmp_nlst;
1202 } else if (strcmp(local, "-") == 0) {
1203 fout = stdout;
1204 } else if (allowpipe && *local == '|') {
1205 oldintp = signal(SIGPIPE, SIG_IGN);
1206 fout = mypopen(local + 1, "w");
1207 if (fout == NULL) {
1208 perror(local+1);
1209 goto abort;
1211 closefunc = mypclose;
1212 } else {
1213 fout = fopen(local, mode);
1214 if (fout == NULL) {
1215 perror(local);
1216 goto abort;
1218 closefunc = fclose;
1220 start = gethrtime();
1221 stop_timer();
1222 switch (type) {
1224 case TYPE_I:
1225 case TYPE_L:
1226 if ((restart_point > 0) && retrcmd &&
1227 lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1228 perror(local);
1229 goto abort;
1231 errno = d = 0;
1232 infd = fileno(din);
1233 while ((c = timedread(infd, buf, FTPBUFSIZ, timeout)) > 0) {
1234 for (n = 0; n < c; n += d) {
1235 d = write(fileno(fout), &buf[n], c - n);
1236 if (d == -1)
1237 goto writeerr;
1239 bytes += c;
1240 if (hash) {
1241 while (bytes >= hashbytes) {
1242 (void) putchar('#');
1243 hashbytes += HASHSIZ;
1245 (void) fflush(stdout);
1248 if (hash && bytes > 0) {
1249 if (bytes < hashbytes)
1250 (void) putchar('#');
1251 (void) putchar('\n');
1252 (void) fflush(stdout);
1254 if (c < 0) {
1255 errflg = 1;
1256 perror("netin");
1258 if ((d < 0) || ((c == 0) && (fsync(fileno(fout)) == -1))) {
1259 writeerr:
1260 errflg = 1;
1261 perror(local);
1263 break;
1265 case TYPE_A:
1266 if ((restart_point > 0) && retrcmd) {
1267 int c;
1268 off_t i = 0;
1270 if (fseek(fout, 0L, SEEK_SET) < 0) {
1271 perror(local);
1272 goto abort;
1274 while (i++ < restart_point) {
1275 if ((c = getc(fout)) == EOF) {
1276 if (ferror(fout))
1277 perror(local);
1278 else
1279 (void) fprintf(stderr,
1280 "%s: Unexpected end of file\n",
1281 local);
1282 goto abort;
1284 if (c == '\n')
1285 i++;
1287 if (fseeko(fout, 0L, SEEK_CUR) < 0) {
1288 perror(local);
1289 goto abort;
1292 fdio_setbuf(buf, FTPBUFSIZ);
1293 infd = fileno(din);
1294 while ((c = fdio_getc(infd)) != EOF) {
1295 while (c == '\r') {
1296 while (hash && (bytes >= hashbytes)) {
1297 (void) putchar('#');
1298 (void) fflush(stdout);
1299 hashbytes += HASHSIZ;
1301 bytes++;
1303 if ((c = fdio_getc(infd)) != '\n' || tcrflag) {
1304 if (ferror(fout))
1305 break;
1306 if (putc('\r', fout) == EOF)
1307 goto writer_ascii_err;
1309 #ifdef notdef
1310 if (c == '\0') {
1311 bytes++;
1312 continue;
1314 #endif
1315 if (c == EOF)
1316 goto endread;
1318 if (putc(c, fout) == EOF)
1319 goto writer_ascii_err;
1320 bytes++;
1322 endread:
1323 if (hash && bytes > 0) {
1324 if (bytes < hashbytes)
1325 (void) putchar('#');
1326 (void) putchar('\n');
1327 (void) fflush(stdout);
1329 if (fdio_error(infd)) {
1330 errflg = 1;
1331 perror("netin");
1333 if ((fflush(fout) == EOF) || ferror(fout) ||
1334 (fsync(fileno(fout)) == -1)) {
1335 writer_ascii_err:
1336 errflg = 1;
1337 perror(local);
1339 break;
1341 reset_timer();
1342 if (closefunc != NULL)
1343 (*closefunc)(fout);
1344 (void) signal(SIGINT, oldintr);
1345 if (oldintp)
1346 (void) signal(SIGPIPE, oldintp);
1347 (void) fclose(din); data = -1;
1348 stop = gethrtime();
1349 (void) getreply(0);
1350 if (bytes > 0 && verbose && !errflg)
1351 ptransfer("received", bytes, start, stop, local, remote);
1352 if (!ctrl_in)
1353 (void) printf("Lost connection\n");
1354 if (oldtype) {
1355 if (!debug)
1356 verbose = 0;
1357 switch (oldtype) {
1358 case TYPE_I:
1359 setbinary(0, NULL);
1360 break;
1361 case TYPE_E:
1362 setebcdic(0, NULL);
1363 break;
1364 case TYPE_L:
1365 settenex(0, NULL);
1366 break;
1368 verbose = oldverbose;
1370 return;
1371 abort:
1373 /* abort using RFC959 recommended IP, SYNC sequence */
1375 stop = gethrtime();
1376 if (oldintp)
1377 (void) signal(SIGPIPE, oldintp);
1378 (void) signal(SIGINT, SIG_IGN);
1379 if (!cpend) {
1380 code = -1;
1381 (void) signal(SIGINT, oldintr);
1382 return;
1385 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
1386 (void) fflush(ctrl_out);
1387 msg = (char)IAC;
1389 * send IAC in urgent mode instead of DM because UNIX places oob
1390 * mark after urgent byte rather than before as now is protocol
1392 if (send(fileno(ctrl_out), &msg, 1, MSG_OOB) != 1) {
1393 perror("abort");
1395 (void) fprintf(ctrl_out, "%cABOR\r\n", DM);
1396 (void) fflush(ctrl_out);
1397 nfds = fileno(ctrl_in) + 1;
1398 FD_ZERO(&mask);
1399 FD_SET(fileno(ctrl_in), &mask);
1400 if (din) {
1401 FD_SET(fileno(din), &mask);
1402 nfds = MAX(fileno(din) + 1, nfds);
1404 if ((nfnd = empty(&mask, 10, nfds)) <= 0) {
1405 if (nfnd < 0) {
1406 perror("abort");
1408 code = -1;
1409 lostpeer(0);
1411 if (din && FD_ISSET(fileno(din), &mask)) {
1412 do {
1413 reset_timer();
1414 } while ((c = read(fileno(din), buf, FTPBUFSIZ)) > 0);
1416 if ((c = getreply(0)) == ERROR && code == 552) {
1417 /* needed for nic style abort */
1418 if (data >= 0) {
1419 (void) close(data);
1420 data = -1;
1422 (void) getreply(0);
1424 if (oldtype) {
1425 if (!debug)
1426 verbose = 0;
1427 switch (oldtype) {
1428 case TYPE_I:
1429 setbinary(0, NULL);
1430 break;
1431 case TYPE_E:
1432 setebcdic(0, NULL);
1433 break;
1434 case TYPE_L:
1435 settenex(0, NULL);
1436 break;
1438 verbose = oldverbose;
1440 (void) getreply(0);
1441 code = -1;
1442 if (data >= 0) {
1443 (void) close(data);
1444 data = -1;
1446 if (closefunc != NULL && fout != NULL)
1447 (*closefunc)(fout);
1448 if (din) {
1449 (void) fclose(din);
1450 data = -1;
1452 if (bytes > 0 && verbose)
1453 ptransfer("received", bytes, start, stop, local, remote);
1454 if (!ctrl_in)
1455 (void) printf("Lost connection\n");
1456 (void) signal(SIGINT, oldintr);
1460 * Need to start a listen on the data channel
1461 * before we send the command, otherwise the
1462 * server's connect may fail.
1465 static int
1466 initconn(void)
1468 unsigned char *p, *a;
1469 int result, tmpno = 0;
1470 int on = 1;
1471 socklen_t len;
1472 int v4_addr;
1473 char *c, *c2, delm;
1474 in_port_t ports;
1476 pasv_refused = B_FALSE;
1477 if (passivemode) {
1478 data = socket(AF_INET6, SOCK_STREAM, 0);
1479 if (data < 0) {
1480 perror("socket");
1481 return (1);
1483 if (timeout && setsockopt(data, IPPROTO_TCP,
1484 TCP_ABORT_THRESHOLD, (char *)&timeoutms,
1485 sizeof (timeoutms)) < 0 && debug)
1486 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
1487 if ((options & SO_DEBUG) &&
1488 setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
1489 sizeof (on)) < 0)
1490 perror("setsockopt (ignored)");
1492 * Use the system wide default send and receive buffer sizes
1493 * unless one has been specified.
1495 if (tcpwindowsize) {
1496 if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
1497 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1498 perror("ftp: setsockopt (SO_SNDBUF - ignored)");
1499 if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
1500 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1501 perror("ftp: setsockopt (SO_RCVBUF - ignored)");
1504 data_addr = remctladdr;
1506 if (ipv6rem == B_TRUE) {
1507 if (command("EPSV") != COMPLETE) {
1508 (void) fprintf(stderr,
1509 "Passive mode refused. Try EPRT\n");
1510 pasv_refused = B_TRUE;
1511 goto noport;
1515 * Get the data port from reply string from the
1516 * server. The format of the reply string is:
1517 * 229 Entering Extended Passive Mode (|||port|)
1518 * where | is the delimiter being used.
1520 c = strchr(reply_string, '(');
1521 c2 = strchr(reply_string, ')');
1522 if (c == NULL || c2 == NULL) {
1523 (void) fprintf(stderr, "Extended passive mode"
1524 "parsing failure.\n");
1525 goto bad;
1527 *(c2 - 1) = NULL;
1528 /* Delimiter is the next char in the reply string */
1529 delm = *(++c);
1530 while (*c == delm) {
1531 if (!*(c++)) {
1532 (void) fprintf(stderr,
1533 "Extended passive mode"
1534 "parsing failure.\n");
1535 goto bad;
1538 /* assign the port for data connection */
1539 ports = (in_port_t)atoi(c);
1540 data_addr.sin6_port = htons(ports);
1541 } else {
1542 int a1, a2, a3, a4, p1, p2;
1544 if (command("PASV") != COMPLETE) {
1545 (void) fprintf(stderr,
1546 "Passive mode refused. Try PORT\n");
1547 pasv_refused = B_TRUE;
1548 goto noport;
1552 * Get the data port from reply string from the
1553 * server. The format of the reply string is:
1554 * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
1556 if (sscanf(pasv, "%d,%d,%d,%d,%d,%d",
1557 &a1, &a2, &a3, &a4, &p1, &p2) != 6) {
1558 (void) fprintf(stderr,
1559 "Passive mode parsing failure.\n");
1560 goto bad;
1563 * Set the supplied address and port in an
1564 * IPv4-mapped IPv6 address.
1566 a = (unsigned char *)&data_addr.sin6_addr +
1567 sizeof (struct in6_addr) -
1568 sizeof (struct in_addr);
1569 #define UC(b) ((b)&0xff)
1570 a[0] = UC(a1);
1571 a[1] = UC(a2);
1572 a[2] = UC(a3);
1573 a[3] = UC(a4);
1574 p = (unsigned char *)&data_addr.sin6_port;
1575 p[0] = UC(p1);
1576 p[1] = UC(p2);
1579 if (connect(data, (struct sockaddr *)&data_addr,
1580 sizeof (data_addr)) < 0) {
1581 perror("connect");
1582 goto bad;
1584 return (0);
1587 noport:
1588 data_addr = myctladdr;
1589 if (sendport)
1590 data_addr.sin6_port = 0; /* let system pick one */
1592 if (data != -1)
1593 (void) close(data);
1594 data = socket(AF_INET6, SOCK_STREAM, 0);
1595 if (data < 0) {
1596 perror("ftp: socket");
1597 if (tmpno)
1598 sendport = 1;
1599 return (1);
1601 if (!sendport)
1602 if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
1603 (char *)&on, sizeof (on)) < 0) {
1604 perror("ftp: setsockopt (SO_REUSEADDR)");
1605 goto bad;
1607 if (bind(data,
1608 (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
1609 perror("ftp: bind");
1610 goto bad;
1612 if (timeout && setsockopt(data, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
1613 (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
1614 perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
1615 if (options & SO_DEBUG &&
1616 setsockopt(data, SOL_SOCKET, SO_DEBUG,
1617 (char *)&on, sizeof (on)) < 0)
1618 perror("ftp: setsockopt (SO_DEBUG - ignored)");
1620 * Use the system wide default send and receive buffer sizes unless
1621 * one has been specified.
1623 if (tcpwindowsize) {
1624 if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
1625 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1626 perror("ftp: setsockopt (SO_SNDBUF - ignored)");
1627 if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
1628 (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
1629 perror("ftp: setsockopt (SO_RCVBUF - ignored)");
1631 len = sizeof (data_addr);
1632 if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
1633 perror("ftp: getsockname");
1634 goto bad;
1637 v4_addr = IN6_IS_ADDR_V4MAPPED(&data_addr.sin6_addr);
1638 if (listen(data, 1) < 0)
1639 perror("ftp: listen");
1641 if (sendport) {
1642 a = (unsigned char *)&data_addr.sin6_addr;
1643 p = (unsigned char *)&data_addr.sin6_port;
1644 if (v4_addr) {
1645 result =
1646 command("PORT %d,%d,%d,%d,%d,%d",
1647 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1648 UC(p[0]), UC(p[1]));
1649 } else {
1650 char hname[INET6_ADDRSTRLEN];
1652 result = COMPLETE + 1;
1654 * if on previous try to server, it was
1655 * determined that the server doesn't support
1656 * EPRT, don't bother trying again. Just try
1657 * LPRT.
1659 if (eport_supported == B_TRUE) {
1660 if (inet_ntop(AF_INET6, &data_addr.sin6_addr,
1661 hname, sizeof (hname)) != NULL) {
1662 result = command("EPRT |%d|%s|%d|", 2,
1663 hname, htons(data_addr.sin6_port));
1664 if (result != COMPLETE)
1665 eport_supported = B_FALSE;
1668 /* Try LPRT */
1669 if (result != COMPLETE) {
1670 result = command(
1671 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
1672 6, 16,
1673 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
1674 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
1675 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
1676 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
1677 2, UC(p[0]), UC(p[1]));
1681 if (result == ERROR && sendport == -1) {
1682 sendport = 0;
1683 tmpno = 1;
1684 goto noport;
1686 return (result != COMPLETE);
1688 if (tmpno)
1689 sendport = 1;
1690 return (0);
1691 bad:
1692 (void) close(data), data = -1;
1693 if (tmpno)
1694 sendport = 1;
1695 return (1);
1698 static FILE *
1699 dataconn(char *mode)
1701 struct sockaddr_in6 from;
1702 int s;
1703 socklen_t fromlen = sizeof (from);
1705 reset_timer();
1706 if (passivemode && !pasv_refused)
1707 return (fdopen(data, mode));
1709 s = accept(data, (struct sockaddr *)&from, &fromlen);
1710 if (s < 0) {
1711 perror("ftp: accept");
1712 (void) close(data), data = -1;
1713 return (NULL);
1715 (void) close(data);
1716 data = s;
1717 return (fdopen(data, mode));
1720 static void
1721 ptransfer(char *direction, off_t bytes, hrtime_t t0,
1722 hrtime_t t1, char *local, char *remote)
1724 hrtime_t td; /* nanoseconds in a 64 bit int */
1725 double s, bs;
1727 td = t1 - t0;
1728 s = (double)td / 1000000000.0; /* seconds */
1729 bs = (double)bytes / NONZERO(s);
1730 if (local && *local != '-')
1731 (void) printf("local: %s ", local);
1732 if (remote)
1733 (void) printf("remote: %s\n", remote);
1734 (void) printf("%lld bytes %s in %.2g seconds (%.2f Kbytes/s)\n",
1735 (longlong_t)bytes, direction, s, bs / 1024.0);
1738 /*ARGSUSED*/
1739 static void
1740 psabort(int sig)
1742 abrtflag++;
1745 void
1746 pswitch(int flag)
1748 void (*oldintr)();
1749 static struct comvars {
1750 int connect;
1751 char name[MAXHOSTNAMELEN];
1752 struct sockaddr_in6 mctl;
1753 struct sockaddr_in6 hctl;
1754 FILE *in;
1755 FILE *out;
1756 int tpe;
1757 int cpnd;
1758 int sunqe;
1759 int runqe;
1760 int mcse;
1761 int ntflg;
1762 char nti[17];
1763 char nto[17];
1764 int mapflg;
1765 char mi[MAXPATHLEN];
1766 char mo[MAXPATHLEN];
1767 int authtype;
1768 int clvl;
1769 int dlvl;
1770 } proxstruct, tmpstruct;
1771 struct comvars *ip, *op;
1773 abrtflag = 0;
1774 oldintr = signal(SIGINT, psabort);
1775 if (flag) {
1776 if (proxy)
1777 return;
1778 ip = &tmpstruct;
1779 op = &proxstruct;
1780 proxy++;
1781 } else {
1782 if (!proxy)
1783 return;
1784 ip = &proxstruct;
1785 op = &tmpstruct;
1786 proxy = 0;
1788 ip->connect = connected;
1789 connected = op->connect;
1790 if (hostname)
1791 (void) strlcpy(ip->name, hostname, sizeof (ip->name));
1792 else
1793 ip->name[0] = 0;
1794 hostname = op->name;
1795 ip->hctl = remctladdr;
1796 remctladdr = op->hctl;
1797 ip->mctl = myctladdr;
1798 myctladdr = op->mctl;
1799 ip->in = ctrl_in;
1800 ctrl_in = op->in;
1801 ip->out = ctrl_out;
1802 ctrl_out = op->out;
1803 ip->tpe = type;
1804 type = op->tpe;
1805 if (!type)
1806 type = 1;
1807 ip->cpnd = cpend;
1808 cpend = op->cpnd;
1809 ip->sunqe = sunique;
1810 sunique = op->sunqe;
1811 ip->runqe = runique;
1812 runique = op->runqe;
1813 ip->mcse = mcase;
1814 mcase = op->mcse;
1815 ip->ntflg = ntflag;
1816 ntflag = op->ntflg;
1817 (void) strlcpy(ip->nti, ntin, sizeof (ip->nti));
1818 (void) strlcpy(ntin, op->nti, sizeof (ntin));
1819 (void) strlcpy(ip->nto, ntout, sizeof (ip->nto));
1820 (void) strlcpy(ntout, op->nto, sizeof (ntout));
1821 ip->mapflg = mapflag;
1822 mapflag = op->mapflg;
1823 (void) strlcpy(ip->mi, mapin, sizeof (ip->mi));
1824 (void) strlcpy(mapin, op->mi, sizeof (mapin));
1825 (void) strlcpy(ip->mo, mapout, sizeof (ip->mo));
1826 (void) strlcpy(mapout, op->mo, sizeof (mapout));
1828 ip->authtype = auth_type;
1829 auth_type = op->authtype;
1830 ip->clvl = clevel;
1831 clevel = op->clvl;
1832 ip->dlvl = dlevel;
1833 dlevel = op->dlvl;
1834 if (!clevel)
1835 clevel = PROT_C;
1836 if (!dlevel)
1837 dlevel = PROT_C;
1839 (void) signal(SIGINT, oldintr);
1840 if (abrtflag) {
1841 abrtflag = 0;
1842 (*oldintr)();
1846 /*ARGSUSED*/
1847 static void
1848 abortpt(int sig)
1850 (void) printf("\n");
1851 (void) fflush(stdout);
1852 ptabflg++;
1853 mflag = 0;
1854 abrtflag = 0;
1855 longjmp(ptabort, 1);
1858 static void
1859 proxtrans(char *cmd, char *local, char *remote)
1861 void (*oldintr)();
1862 int tmptype, oldtype = 0, secndflag = 0, nfnd;
1863 extern jmp_buf ptabort;
1864 char *cmd2;
1865 struct fd_set mask;
1866 int ipv4_addr = IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr);
1868 if (strcmp(cmd, "RETR"))
1869 cmd2 = "RETR";
1870 else
1871 cmd2 = runique ? "STOU" : "STOR";
1872 if (command(ipv4_addr ? "PASV" : "EPSV") != COMPLETE) {
1873 (void) printf(
1874 "proxy server does not support third part transfers.\n");
1875 return;
1877 tmptype = type;
1878 pswitch(0);
1879 if (!connected) {
1880 (void) printf("No primary connection\n");
1881 pswitch(1);
1882 code = -1;
1883 return;
1885 if (type != tmptype) {
1886 oldtype = type;
1887 switch (tmptype) {
1888 case TYPE_A:
1889 setascii(0, NULL);
1890 break;
1891 case TYPE_I:
1892 setbinary(0, NULL);
1893 break;
1894 case TYPE_E:
1895 setebcdic(0, NULL);
1896 break;
1897 case TYPE_L:
1898 settenex(0, NULL);
1899 break;
1902 if (command(ipv4_addr ? "PORT %s" : "EPRT %s", pasv) != COMPLETE) {
1903 switch (oldtype) {
1904 case 0:
1905 break;
1906 case TYPE_A:
1907 setascii(0, NULL);
1908 break;
1909 case TYPE_I:
1910 setbinary(0, NULL);
1911 break;
1912 case TYPE_E:
1913 setebcdic(0, NULL);
1914 break;
1915 case TYPE_L:
1916 settenex(0, NULL);
1917 break;
1919 pswitch(1);
1920 return;
1922 if (setjmp(ptabort))
1923 goto abort;
1924 oldintr = signal(SIGINT, (void (*)())abortpt);
1925 if (command("%s %s", cmd, remote) != PRELIM) {
1926 (void) signal(SIGINT, oldintr);
1927 switch (oldtype) {
1928 case 0:
1929 break;
1930 case TYPE_A:
1931 setascii(0, NULL);
1932 break;
1933 case TYPE_I:
1934 setbinary(0, NULL);
1935 break;
1936 case TYPE_E:
1937 setebcdic(0, NULL);
1938 break;
1939 case TYPE_L:
1940 settenex(0, NULL);
1941 break;
1943 pswitch(1);
1944 return;
1946 (void) sleep(2);
1947 pswitch(1);
1948 secndflag++;
1949 if (command("%s %s", cmd2, local) != PRELIM)
1950 goto abort;
1951 ptflag++;
1952 (void) getreply(0);
1953 pswitch(0);
1954 (void) getreply(0);
1955 (void) signal(SIGINT, oldintr);
1956 switch (oldtype) {
1957 case 0:
1958 break;
1959 case TYPE_A:
1960 setascii(0, NULL);
1961 break;
1962 case TYPE_I:
1963 setbinary(0, NULL);
1964 break;
1965 case TYPE_E:
1966 setebcdic(0, NULL);
1967 break;
1968 case TYPE_L:
1969 settenex(0, NULL);
1970 break;
1972 pswitch(1);
1973 ptflag = 0;
1974 (void) printf("local: %s remote: %s\n", local, remote);
1975 return;
1976 abort:
1977 (void) signal(SIGINT, SIG_IGN);
1978 ptflag = 0;
1979 if (strcmp(cmd, "RETR") && !proxy)
1980 pswitch(1);
1981 else if ((strcmp(cmd, "RETR") == 0) && proxy)
1982 pswitch(0);
1983 if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
1984 if (command("%s %s", cmd2, local) != PRELIM) {
1985 pswitch(0);
1986 switch (oldtype) {
1987 case 0:
1988 break;
1989 case TYPE_A:
1990 setascii(0, NULL);
1991 break;
1992 case TYPE_I:
1993 setbinary(0, NULL);
1994 break;
1995 case TYPE_E:
1996 setebcdic(0, NULL);
1997 break;
1998 case TYPE_L:
1999 settenex(0, NULL);
2000 break;
2002 if (cpend) {
2003 char msg[2];
2005 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2006 (void) fflush(ctrl_out);
2007 *msg = (char)IAC;
2008 *(msg+1) = (char)DM;
2009 if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
2010 != 2)
2011 perror("abort");
2012 (void) fprintf(ctrl_out, "ABOR\r\n");
2013 (void) fflush(ctrl_out);
2014 FD_ZERO(&mask);
2015 FD_SET(fileno(ctrl_in), &mask);
2016 if ((nfnd = empty(&mask, 10,
2017 fileno(ctrl_in) + 1)) <= 0) {
2018 if (nfnd < 0) {
2019 perror("abort");
2021 if (ptabflg)
2022 code = -1;
2023 lostpeer(0);
2025 (void) getreply(0);
2026 (void) getreply(0);
2029 pswitch(1);
2030 if (ptabflg)
2031 code = -1;
2032 (void) signal(SIGINT, oldintr);
2033 return;
2035 if (cpend) {
2036 char msg[2];
2038 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2039 (void) fflush(ctrl_out);
2040 *msg = (char)IAC;
2041 *(msg+1) = (char)DM;
2042 if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
2043 perror("abort");
2044 (void) fprintf(ctrl_out, "ABOR\r\n");
2045 (void) fflush(ctrl_out);
2046 FD_ZERO(&mask);
2047 FD_SET(fileno(ctrl_in), &mask);
2048 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2049 if (nfnd < 0) {
2050 perror("abort");
2052 if (ptabflg)
2053 code = -1;
2054 lostpeer(0);
2056 (void) getreply(0);
2057 (void) getreply(0);
2059 pswitch(!proxy);
2060 if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
2061 if (command("%s %s", cmd2, local) != PRELIM) {
2062 pswitch(0);
2063 switch (oldtype) {
2064 case 0:
2065 break;
2066 case TYPE_A:
2067 setascii(0, NULL);
2068 break;
2069 case TYPE_I:
2070 setbinary(0, NULL);
2071 break;
2072 case TYPE_E:
2073 setebcdic(0, NULL);
2074 break;
2075 case TYPE_L:
2076 settenex(0, NULL);
2077 break;
2079 if (cpend) {
2080 char msg[2];
2082 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2083 (void) fflush(ctrl_out);
2084 *msg = (char)IAC;
2085 *(msg+1) = (char)DM;
2086 if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
2087 != 2)
2088 perror("abort");
2089 (void) fprintf(ctrl_out, "ABOR\r\n");
2090 (void) fflush(ctrl_out);
2091 FD_ZERO(&mask);
2092 FD_SET(fileno(ctrl_in), &mask);
2093 if ((nfnd = empty(&mask, 10,
2094 fileno(ctrl_in) + 1)) <= 0) {
2095 if (nfnd < 0) {
2096 perror("abort");
2098 if (ptabflg)
2099 code = -1;
2100 lostpeer(0);
2102 (void) getreply(0);
2103 (void) getreply(0);
2105 pswitch(1);
2106 if (ptabflg)
2107 code = -1;
2108 (void) signal(SIGINT, oldintr);
2109 return;
2112 if (cpend) {
2113 char msg[2];
2115 (void) fprintf(ctrl_out, "%c%c", IAC, IP);
2116 (void) fflush(ctrl_out);
2117 *msg = (char)IAC;
2118 *(msg+1) = (char)DM;
2119 if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
2120 perror("abort");
2121 (void) fprintf(ctrl_out, "ABOR\r\n");
2122 (void) fflush(ctrl_out);
2123 FD_ZERO(&mask);
2124 FD_SET(fileno(ctrl_in), &mask);
2125 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2126 if (nfnd < 0) {
2127 perror("abort");
2129 if (ptabflg)
2130 code = -1;
2131 lostpeer(0);
2133 (void) getreply(0);
2134 (void) getreply(0);
2136 pswitch(!proxy);
2137 if (cpend) {
2138 FD_ZERO(&mask);
2139 FD_SET(fileno(ctrl_in), &mask);
2140 if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
2141 if (nfnd < 0) {
2142 perror("abort");
2144 if (ptabflg)
2145 code = -1;
2146 lostpeer(0);
2148 (void) getreply(0);
2149 (void) getreply(0);
2151 if (proxy)
2152 pswitch(0);
2153 switch (oldtype) {
2154 case 0:
2155 break;
2156 case TYPE_A:
2157 setascii(0, NULL);
2158 break;
2159 case TYPE_I:
2160 setbinary(0, NULL);
2161 break;
2162 case TYPE_E:
2163 setebcdic(0, NULL);
2164 break;
2165 case TYPE_L:
2166 settenex(0, NULL);
2167 break;
2169 pswitch(1);
2170 if (ptabflg)
2171 code = -1;
2172 (void) signal(SIGINT, oldintr);
2175 /*ARGSUSED*/
2176 void
2177 reset(int argc, char *argv[])
2179 struct fd_set mask;
2180 int nfnd = 1;
2182 FD_ZERO(&mask);
2183 while (nfnd > 0) {
2184 FD_SET(fileno(ctrl_in), &mask);
2185 if ((nfnd = empty(&mask, 0, fileno(ctrl_in) + 1)) < 0) {
2186 perror("reset");
2187 code = -1;
2188 lostpeer(0);
2189 } else if (nfnd > 0) {
2190 (void) getreply(0);
2195 static char *
2196 gunique(char *local)
2198 static char new[MAXPATHLEN];
2199 char *cp = rindex(local, '/');
2200 int d, count = 0;
2201 char ext = '1';
2203 if (cp)
2204 *cp = '\0';
2205 d = access(cp ? local : ".", 2);
2206 if (cp)
2207 *cp = '/';
2208 if (d < 0) {
2209 perror(local);
2210 return ((char *)0);
2212 if (strlcpy(new, local, sizeof (new)) >= sizeof (new))
2213 (void) printf("gunique: too long: local %s, %d, new %d\n",
2214 local, strlen(local), sizeof (new));
2216 cp = new + strlen(new);
2217 *cp++ = '.';
2218 while (!d) {
2219 if (++count == 100) {
2220 (void) printf(
2221 "runique: can't find unique file name.\n");
2222 return ((char *)0);
2224 *cp++ = ext;
2225 *cp = '\0';
2226 if (ext == '9')
2227 ext = '0';
2228 else
2229 ext++;
2230 if ((d = access(new, 0)) < 0)
2231 break;
2232 if (ext != '0')
2233 cp--;
2234 else if (*(cp - 2) == '.')
2235 *(cp - 1) = '1';
2236 else {
2237 *(cp - 2) = *(cp - 2) + 1;
2238 cp--;
2241 return (new);
2245 * This is a wrap-around function for inet_ntop(). In case the af is AF_INET6
2246 * and the address pointed by src is a IPv4-mapped IPv6 address, it
2247 * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
2248 * it behaves just like inet_ntop().
2250 const char *
2251 inet_ntop_native(int af, const void *src, char *dst, size_t size)
2253 struct in_addr src4;
2254 const char *result;
2255 struct sockaddr_in *sin;
2256 struct sockaddr_in6 *sin6;
2258 if (af == AF_INET6) {
2259 sin6 = (struct sockaddr_in6 *)src;
2260 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
2261 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &src4);
2262 result = inet_ntop(AF_INET, &src4, dst, size);
2263 } else {
2264 result = inet_ntop(AF_INET6, &sin6->sin6_addr,
2265 dst, size);
2267 } else {
2268 sin = (struct sockaddr_in *)src;
2269 result = inet_ntop(af, &sin->sin_addr, dst, size);
2272 return (result);
2276 secure_command(char *cmd)
2278 unsigned char *in = NULL, *out = NULL;
2279 int length = 0;
2280 size_t inlen;
2282 if ((auth_type != AUTHTYPE_NONE) && clevel != PROT_C) {
2283 gss_buffer_desc in_buf, out_buf;
2284 OM_uint32 maj_stat, min_stat;
2286 /* secure_command (based on level) */
2287 if (auth_type == AUTHTYPE_GSSAPI) {
2288 OM_uint32 expire_time;
2289 int conf_state;
2290 /* clevel = PROT_P; */
2291 in_buf.value = cmd;
2292 in_buf.length = strlen(cmd) + 1;
2294 maj_stat = gss_context_time(&min_stat, gcontext,
2295 &expire_time);
2296 if (GSS_ERROR(maj_stat)) {
2297 user_gss_error(maj_stat, min_stat,
2298 "gss context has expired");
2299 fatal("Your gss credentials have expired. "
2300 "Good-bye!");
2302 maj_stat = gss_seal(&min_stat, gcontext,
2303 (clevel == PROT_P), /* private */
2304 GSS_C_QOP_DEFAULT,
2305 &in_buf, &conf_state,
2306 &out_buf);
2307 if (maj_stat != GSS_S_COMPLETE) {
2308 /* generally need to deal */
2309 user_gss_error(maj_stat, min_stat,
2310 (clevel == PROT_P) ?
2311 "gss_seal ENC didn't complete":
2312 "gss_seal MIC didn't complete");
2313 } else if ((clevel == PROT_P) && !conf_state) {
2314 (void) fprintf(stderr,
2315 "GSSAPI didn't encrypt message");
2316 out = out_buf.value;
2317 } else {
2318 if (debug)
2319 (void) fprintf(stderr,
2320 "sealed (%s) %d bytes\n",
2321 clevel == PROT_P ? "ENC" : "MIC",
2322 out_buf.length);
2324 out = out_buf.value;
2327 /* Other auth types go here ... */
2328 inlen = ((4 * out_buf.length) / 3) + 4;
2329 in = (uchar_t *)malloc(inlen);
2330 if (in == NULL) {
2331 gss_release_buffer(&min_stat, &out_buf);
2332 fatal("Memory error allocating space for response.");
2334 length = out_buf.length;
2335 if (auth_error = radix_encode(out, in, inlen, &length, 0)) {
2336 (void) fprintf(stderr,
2337 "Couldn't base 64 encode command (%s)\n",
2338 radix_error(auth_error));
2339 free(in);
2340 gss_release_buffer(&min_stat, &out_buf);
2341 return (0);
2344 (void) fprintf(ctrl_out, "%s %s",
2345 clevel == PROT_P ? "ENC" : "MIC", in);
2347 free(in);
2348 gss_release_buffer(&min_stat, &out_buf);
2350 if (debug)
2351 (void) fprintf(stderr,
2352 "secure_command(%s)\nencoding %d bytes %s %s\n",
2353 cmd, length,
2354 (clevel == PROT_P) ? "ENC" : "MIC", in);
2355 } else {
2357 * auth_type = AUTHTYPE_NONE or
2358 * command channel is not protected
2360 fputs(cmd, ctrl_out);
2363 (void) fprintf(ctrl_out, "\r\n");
2364 (void) fflush(ctrl_out);
2365 return (1);
2368 unsigned int maxbuf;
2369 unsigned char *ucbuf;
2371 void
2372 setpbsz(unsigned int size)
2374 unsigned int actualbuf;
2375 int oldverbose;
2377 if (ucbuf)
2378 (void) free(ucbuf);
2379 actualbuf = size;
2380 while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL) {
2381 if (actualbuf)
2382 actualbuf >>= 2;
2383 else {
2384 perror("Error while trying to malloc PROT buffer:");
2385 exit(1);
2388 oldverbose = verbose;
2389 verbose = 0;
2390 reply_parse = "PBSZ=";
2391 if (command("PBSZ %u", actualbuf) != COMPLETE)
2392 fatal("Cannot set PROT buffer size");
2393 if (reply_parse) {
2394 if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
2395 maxbuf = actualbuf;
2396 } else
2397 maxbuf = actualbuf;
2398 reply_parse = NULL;
2399 verbose = oldverbose;
2403 * Do the base 64 decoding of the raw input buffer, b64_buf.
2404 * Also do the verification and decryption, if required.
2405 * retval contains the current error code number
2407 * returns:
2408 * (RFC 2228: error returns are 3 digit numbers of the form 5xy)
2409 * 5 if an error occurred
2411 static int
2412 decode_reply(uchar_t *plain_buf,
2413 int ilen,
2414 uchar_t *b64_buf,
2415 int retval,
2416 boolean_t *again)
2418 int len;
2419 int safe = 0;
2421 *again = 0;
2423 if (!b64_buf[0]) /* if there is no string, no problem */
2424 return (retval);
2426 if ((auth_type == AUTHTYPE_NONE)) {
2427 (void) printf("Cannot decode reply:\n%d %s\n", code, b64_buf);
2428 return ('5');
2431 switch (code) {
2433 case 631: /* 'safe' */
2434 safe = 1;
2435 break;
2437 case 632: /* 'private' */
2438 break;
2440 case 633: /* 'confidential' */
2441 break;
2443 default:
2444 (void) printf("Unknown reply: %d %s\n", code, b64_buf);
2445 return ('5');
2448 /* decode the base64 encoded message */
2449 auth_error = radix_encode(b64_buf, plain_buf, ilen, &len, 1);
2451 if (auth_error) {
2452 (void) printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
2453 code, radix_error(auth_error), b64_buf);
2454 return ('5');
2457 if (auth_type == AUTHTYPE_GSSAPI) {
2458 gss_buffer_desc xmit_buf, msg_buf;
2459 OM_uint32 maj_stat, min_stat;
2460 int conf_state = safe;
2461 xmit_buf.value = plain_buf;
2462 xmit_buf.length = len;
2464 /* decrypt/verify the message */
2465 maj_stat = gss_unseal(&min_stat, gcontext,
2466 &xmit_buf, &msg_buf, &conf_state, NULL);
2467 if (maj_stat != GSS_S_COMPLETE) {
2468 user_gss_error(maj_stat, min_stat,
2469 "failed unsealing reply");
2470 return ('5');
2472 if (msg_buf.length < ilen - 2 - 1) {
2473 memcpy(plain_buf, msg_buf.value, msg_buf.length);
2474 strcpy((char *)&plain_buf[msg_buf.length], "\r\n");
2475 gss_release_buffer(&min_stat, &msg_buf);
2476 *again = 1;
2477 } else {
2478 user_gss_error(maj_stat, min_stat,
2479 "reply was too long");
2480 return ('5');
2482 } /* end if GSSAPI */
2484 /* Other auth types go here... */
2486 return (retval);