xref: /titanic_50/usr/src/cmd/cmd-inet/usr.bin/rcp.c (revision 71269a2275bf5a143dad6461eee2710a344e7261)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright (c) 1983 The Regents of the University of California.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms are permitted
13  * provided that the above copyright notice and this paragraph are
14  * duplicated in all such forms and that any documentation,
15  * advertising materials, and other materials related to such
16  * distribution and use acknowledge that the software was developed
17  * by the University of California, Berkeley.  The name of the
18  * University may not be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  */
22 
23 #define	_FILE_OFFSET_BITS  64
24 
25 /*
26  * rcp
27  */
28 #include <sys/param.h>
29 #include <sys/file.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/acl.h>
35 #include <dirent.h>
36 #include <signal.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <pwd.h>
40 #include <netdb.h>
41 #include <wchar.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <locale.h>
45 #include <strings.h>
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <limits.h>
51 #include <priv_utils.h>
52 #include <sys/sendfile.h>
53 #include <sys/sysmacros.h>
54 #include <sys/wait.h>
55 #include <aclutils.h>
56 #include <sys/varargs.h>
57 
58 /*
59  * It seems like Berkeley got these from pathnames.h?
60  */
61 #define	_PATH_RSH	"/usr/bin/rsh"
62 #define	_PATH_CP	"/usr/bin/cp"
63 
64 #define	ACL_FAIL	1
65 #define	ACL_OK		0
66 #define	RCP_BUFSIZE	(64 * 1024)
67 
68 #define	RCP_ACL	"/usr/lib/sunw,rcp"
69 		/* see PSARC/1993/004/opinion */
70 
71 typedef struct _buf {
72 	int	cnt;
73 	char	*buf;
74 } BUF;
75 
76 static char *cmd_sunw;
77 static struct passwd *pwd;
78 static int errs;
79 static int pflag;
80 static uid_t userid;
81 static int rem;
82 static int zflag;
83 static int iamremote;
84 static int iamrecursive;
85 static int targetshouldbedirectory;
86 static int aclflag;
87 static int acl_aclflag;
88 static int retval = 0;
89 static int portnumber = 0;
90 
91 static void lostconn(void);
92 static char *search_char(unsigned char *, unsigned char);
93 static char *removebrackets(char *);
94 static char *colon(char *);
95 static int response(void);
96 static void usage(void);
97 static void source(int, char **);
98 static void sink(int, char **);
99 static void toremote(char *, int, char **);
100 static void tolocal(int, char **);
101 static void verifydir(char *);
102 static int okname(char *);
103 static int susystem(char *, char **);
104 static void rsource(char *, struct stat *);
105 static int sendacl(int);
106 static int recvacl(int, int, int);
107 static int zwrite(int, char *, int);
108 static void zopen(int, int);
109 static int zclose(int);
110 static int notzero(char *, int);
111 static BUF *allocbuf(BUF *, int, int);
112 static void error(char *fmt, ...);
113 static void addargs(char **, ...);
114 
115 /*
116  * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF
117  * bytes of data. We would like the size to be aligned to the nearest
118  * MAXBOFFSET (8192) boundary for optimal performance.
119  */
120 #define	SENDFILE_SIZE	0x7FFFE000
121 
122 #include <k5-int.h>
123 #include <profile/prof_int.h>
124 #include <com_err.h>
125 #include <kcmd.h>
126 
127 #define	NULLBUF	(BUF *) 0
128 #define	MAXARGS	10	/* Number of arguments passed to execv() */
129 
130 static int sock;
131 static char *cmd, *cmd_orig, *cmd_sunw_orig;
132 static char *krb_realm = NULL;
133 static char *krb_cache = NULL;
134 static char *krb_config = NULL;
135 static char des_inbuf[2 * RCP_BUFSIZE];
136 				/* needs to be > largest read size */
137 static char des_outbuf[2 * RCP_BUFSIZE];
138 				/* needs to be > largest write size */
139 
140 static krb5_data desinbuf, desoutbuf;
141 static krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
142 static krb5_keyblock *session_key;	/* static key for session */
143 static krb5_context bsd_context;
144 static krb5_auth_context auth_context;
145 static krb5_flags authopts;
146 static krb5_error_code status;
147 
148 static void try_normal_rcp(int, char **);
149 static int init_service(int);
150 static char **save_argv(int, char **);
151 static void answer_auth(char *, char *);
152 static int desrcpwrite(int, char *, int);
153 static int desrcpread(int, char *, int);
154 
155 /*
156  * Not sure why these two don't have their own header file declarations, but
157  * lint complains about absent declarations so place some here. Sigh.
158  */
159 extern errcode_t	profile_get_options_boolean(profile_t, char **,
160     profile_options_boolean *);
161 extern errcode_t	profile_get_options_string(profile_t, char **,
162     profile_option_strings *);
163 
164 static int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
165 static int encrypt_flag = 0;	/* Flag set, when encryption is enabled */
166 static int encrypt_done = 0;	/* Flag set, if "-x" is specified */
167 static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
168 
169 /* Flag set, if -PN / -PO is specified */
170 static boolean_t rcmdoption_done = B_FALSE;
171 
172 static profile_options_boolean option[] = {
173 	{ "encrypt", &encrypt_flag, 0 },
174 	{ NULL, NULL, 0 }
175 };
176 
177 static char *rcmdproto = NULL;
178 static profile_option_strings rcmdversion[] = {
179 	{ "rcmd_protocol", &rcmdproto, 0 },
180 	{ NULL, NULL, 0 }
181 };
182 
183 static char *realmdef[] = { "realms", NULL, "rcp", NULL };
184 static char *appdef[] = { "appdefaults", "rcp", NULL };
185 static char **prev_argv;
186 static int prev_argc;
187 
188 int
189 main(int argc, char *argv[])
190 {
191 	int ch, fflag, tflag;
192 	char *targ;
193 	size_t cmdsiz;
194 
195 	(void) setlocale(LC_ALL, "");
196 
197 	if (strcmp(argv[0], RCP_ACL) == 0)
198 		aclflag = 1;
199 
200 	if (!(pwd = getpwuid(userid = getuid()))) {
201 		(void) fprintf(stderr, "rcp: unknown user %d.\n",
202 		    (uint_t)userid);
203 		return (1);
204 	}
205 
206 	fflag = tflag = 0;
207 	while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:Z")) != EOF) {
208 		switch (ch) {
209 		case 'd':
210 			targetshouldbedirectory = 1;
211 			break;
212 		case 'f':			/* "from" */
213 			fflag = 1;
214 			if (aclflag | acl_aclflag)
215 				/* ok response */
216 				(void) desrcpwrite(rem, "", 1);
217 			break;
218 		case 'p':			/* preserve access/mod times */
219 			++pflag;
220 			break;
221 		case 'r':
222 			++iamrecursive;
223 			break;
224 		case 't':			/* "to" */
225 			tflag = 1;
226 			break;
227 		case 'Z':
228 			acl_aclflag++;
229 			break;
230 		case 'x':
231 			if (!krb5_privacy_allowed()) {
232 				(void) fprintf(stderr, gettext("rcp: "
233 					"Encryption not supported.\n"));
234 				return (1);
235 			}
236 			encrypt_flag++;
237 			krb5auth_flag++;
238 			encrypt_done++;
239 			break;
240 		case 'k':
241 			if ((krb_realm = (char *)strdup(optarg)) == NULL) {
242 				(void) fprintf(stderr, gettext("rcp:"
243 					" Cannot malloc.\n"));
244 				return (1);
245 			}
246 			krb5auth_flag++;
247 			break;
248 		case 'P':
249 			if (strncmp(optarg, "O", 1) == 0) {
250 				if (rcmdoption_done == B_TRUE) {
251 					(void) fprintf(stderr, gettext("rcp: "
252 						"Only one of -PN and -PO "
253 						"allowed.\n"));
254 					usage();
255 				}
256 				kcmd_proto = KCMD_OLD_PROTOCOL;
257 				rcmdoption_done = B_TRUE;
258 			} else if (strncmp(optarg, "N", 1) == 0) {
259 				if (rcmdoption_done == B_TRUE) {
260 					(void) fprintf(stderr, gettext("rcp: "
261 						"Only one of -PN and -PO "
262 						"allowed.\n"));
263 					usage();
264 				}
265 				kcmd_proto = KCMD_NEW_PROTOCOL;
266 				rcmdoption_done = B_TRUE;
267 			} else {
268 				usage();
269 			}
270 			krb5auth_flag++;
271 			break;
272 		case 'a':
273 			krb5auth_flag++;
274 			break;
275 #ifdef DEBUG
276 		case 'D':
277 			portnumber = htons(atoi(optarg));
278 			krb5auth_flag++;
279 			break;
280 #endif /* DEBUG */
281 		case '?':
282 		default:
283 			usage();
284 		}
285 	}
286 	argc -= optind;
287 	argv += optind;
288 
289 	if (krb5auth_flag > 0) {
290 		status = krb5_init_context(&bsd_context);
291 		if (status) {
292 			com_err("rcp", status,
293 				gettext("while initializing krb5"));
294 			return (1);
295 		}
296 
297 		/*
298 		 * Set up buffers for desread and deswrite.
299 		 */
300 		desinbuf.data = des_inbuf;
301 		desoutbuf.data = des_outbuf;
302 		desinbuf.length = sizeof (des_inbuf);
303 		desoutbuf.length = sizeof (des_outbuf);
304 	}
305 
306 	if (fflag || tflag)
307 		if (encrypt_flag > 0)
308 			(void) answer_auth(krb_config, krb_cache);
309 
310 	if (fflag) {
311 		iamremote = 1;
312 		(void) response();
313 		(void) setuid(userid);
314 		source(argc, argv);
315 		return (errs);
316 	}
317 
318 	if (tflag) {
319 		iamremote = 1;
320 		(void) setuid(userid);
321 		sink(argc, argv);
322 		return (errs);
323 	}
324 
325 	if (argc < 2)
326 		usage();
327 
328 	/* This will make "rcmd_af()" magically get the proper privilege */
329 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) {
330 		(void) fprintf(stderr, "rcp: must be set-uid root\n");
331 		exit(1);
332 	}
333 
334 	if (krb5auth_flag > 0) {
335 		/*
336 		 * Get our local realm to look up local realm options.
337 		 */
338 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
339 		if (status) {
340 			com_err("rcp", status,
341 				gettext("while getting default realm"));
342 			return (1);
343 		}
344 		/*
345 		 * See if encryption should be done for this realm
346 		 */
347 		profile_get_options_boolean(bsd_context->profile, realmdef,
348 						option);
349 		/*
350 		 * Check the appdefaults section
351 		 */
352 		profile_get_options_boolean(bsd_context->profile, appdef,
353 						option);
354 		profile_get_options_string(bsd_context->profile, appdef,
355 						rcmdversion);
356 		if ((encrypt_done > 0) || (encrypt_flag > 0)) {
357 			if (krb5_privacy_allowed() == TRUE) {
358 				encrypt_flag++;
359 			} else {
360 				(void) fprintf(stderr, gettext("rcp: Encryption"
361 							" not supported.\n"));
362 				return (1);
363 			}
364 		}
365 
366 		if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
367 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
368 				kcmd_proto = KCMD_NEW_PROTOCOL;
369 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
370 				kcmd_proto = KCMD_OLD_PROTOCOL;
371 			} else {
372 				(void) fprintf(stderr, gettext("Unrecognized "
373 					"KCMD protocol (%s)"), rcmdproto);
374 				return (1);
375 			}
376 		}
377 	}
378 
379 	if (argc > 2)
380 		targetshouldbedirectory = 1;
381 
382 	rem = -1;
383 
384 	if (portnumber == 0) {
385 		if (krb5auth_flag > 0) {
386 			retval = init_service(krb5auth_flag);
387 			if (!retval) {
388 				/*
389 				 * Connecting to the kshell service failed,
390 				 * fallback to normal rcp & reset KRB5 flags.
391 				 */
392 				krb5auth_flag = encrypt_flag = 0;
393 				encrypt_done = 0;
394 				(void) init_service(krb5auth_flag);
395 			}
396 		}
397 		else
398 			(void) init_service(krb5auth_flag);
399 	}
400 
401 #ifdef DEBUG
402 	if (retval || krb5auth_flag) {
403 		(void) fprintf(stderr, gettext("Kerberized rcp session, "
404 				"port %d in use "), portnumber);
405 		if (kcmd_proto == KCMD_OLD_PROTOCOL)
406 			(void) fprintf(stderr, gettext("[kcmd ver.1]\n"));
407 		else
408 			(void) fprintf(stderr, gettext("[kcmd ver.2]\n"));
409 	} else {
410 		(void) fprintf(stderr, gettext("Normal rcp session, port %d "
411 				"in use.\n"), portnumber);
412 	}
413 #endif /* DEBUG */
414 
415 	if (krb5auth_flag > 0) {
416 		/*
417 		 * We calculate here a buffer size that can be used in the
418 		 * allocation of the three buffers cmd, cmd_orig and
419 		 * cmd_sunw_orig that are used to hold different incantations
420 		 * of rcp.
421 		 */
422 		cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") +
423 		    strlen(krb_realm != NULL ? krb_realm : ""),
424 		    sizeof (RCP_ACL " -r -p -z -d"));
425 
426 		if (((cmd = (char *)malloc(cmdsiz)) == NULL) ||
427 			((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) ||
428 			((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) {
429 			(void) fprintf(stderr, gettext("rcp: Cannot "
430 					"malloc.\n"));
431 			return (1);
432 		}
433 
434 		(void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s",
435 			encrypt_flag ? "-x " : "",
436 			iamrecursive ? " -r" : "", pflag ? " -p" : "",
437 			targetshouldbedirectory ? " -d" : "",
438 			krb_realm != NULL ? " -k " : "",
439 			krb_realm != NULL ? krb_realm : "");
440 
441 		/*
442 		 * We would use cmd-orig as the 'cmd-buffer' if kerberized
443 		 * rcp fails, in which case we fallback to normal rcp. We also
444 		 * save argc & argv for the same purpose
445 		 */
446 		(void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s",
447 			iamrecursive ? " -r" : "",
448 			pflag ? " -p" : "",
449 			zflag ? " -z" : "",
450 			targetshouldbedirectory ? " -d" : "");
451 
452 		(void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL,
453 			iamrecursive ? " -r" : "",
454 			pflag ? " -p" : "",
455 			zflag ? " -z" : "",
456 			targetshouldbedirectory ? " -d" : "");
457 
458 		prev_argc = argc;
459 		prev_argv = save_argv(argc, argv);
460 
461 	} else {
462 		cmdsiz = sizeof ("rcp -r -p -z -d");
463 		if (((cmd = (char *)malloc(cmdsiz)) == NULL)) {
464 			(void) fprintf(stderr, gettext("rcp: Cannot "
465 					"malloc.\n"));
466 			return (1);
467 		}
468 
469 		(void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s",
470 			iamrecursive ? " -r" : "",
471 			pflag ? " -p" : "",
472 			zflag ? " -z" : "",
473 			targetshouldbedirectory ? " -d" : "");
474 	}
475 
476 	cmdsiz = sizeof (RCP_ACL " -r -p -z -d");
477 	if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) {
478 		(void) fprintf(stderr, gettext("rcp: Cannot malloc.\n"));
479 		return (1);
480 	}
481 
482 	(void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL,
483 	    iamrecursive ? " -r" : "",
484 	    pflag ? " -p" : "",
485 	    zflag ? " -z" : "",
486 	    targetshouldbedirectory ? " -d" : "");
487 
488 	(void) signal(SIGPIPE, (void (*)(int))lostconn);
489 
490 	if (targ = colon(argv[argc - 1]))
491 		toremote(targ, argc, argv);
492 	else {
493 		tolocal(argc, argv);
494 		if (targetshouldbedirectory)
495 			verifydir(argv[argc - 1]);
496 	}
497 
498 	return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
499 }
500 
501 
502 static void
503 toremote(char *targ, int argc, char *argv[])
504 {
505 	int i;
506 	char *host, *src, *suser, *thost, *tuser;
507 	char resp;
508 	size_t buffersize;
509 	char bp[RCP_BUFSIZE];
510 	krb5_creds *cred;
511 	char *arglist[MAXARGS+1];
512 	buffersize = RCP_BUFSIZE;
513 
514 	*targ++ = 0;
515 	if (*targ == 0)
516 		targ = ".";
517 
518 	if (thost = search_char((unsigned char *)argv[argc - 1], '@')) {
519 		*thost++ = 0;
520 		tuser = argv[argc - 1];
521 		if (*tuser == '\0')
522 			tuser = NULL;
523 		else if (!okname(tuser))
524 			exit(1);
525 	} else {
526 		thost = argv[argc - 1];
527 		tuser = NULL;
528 	}
529 	thost = removebrackets(thost);
530 
531 	for (i = 0; i < argc - 1; i++) {
532 		src = colon(argv[i]);
533 		if (src) {			/* remote to remote */
534 			*src++ = 0;
535 			if (*src == 0)
536 				src = ".";
537 			host = search_char((unsigned char *)argv[i], '@');
538 			if (host) {
539 				*host++ = 0;
540 				host = removebrackets(host);
541 				suser = argv[i];
542 				if (*suser == '\0') {
543 					suser = pwd->pw_name;
544 				} else if (!okname(suser)) {
545 					errs++;
546 					continue;
547 				}
548 				(void) snprintf(bp, buffersize, "'%s%s%s:%s'",
549 				    tuser ? tuser : "", tuser ? "@" : "",
550 				    thost, targ);
551 				(void) addargs(arglist, "rsh", host, "-l",
552 				    suser, "-n", cmd, src, bp, (char *)NULL);
553 			} else {
554 				host = removebrackets(argv[i]);
555 				(void) snprintf(bp, buffersize, "'%s%s%s:%s'",
556 				    tuser ? tuser : "", tuser ? "@" : "",
557 				    thost, targ);
558 				(void) addargs(arglist, "rsh", host, "-n", cmd,
559 				    src, bp, (char *)NULL);
560 			}
561 			if (susystem(_PATH_RSH, arglist) == -1)
562 				errs++;
563 		} else {			/* local to remote */
564 			if (rem == -1) {
565 				host = thost;
566 				if (krb5auth_flag > 0) {
567 
568 				(void) snprintf(bp, buffersize,
569 						"%s -t %s", cmd, targ);
570 				authopts = AP_OPTS_MUTUAL_REQUIRED;
571 				status = kcmd(&sock, &host,
572 					    portnumber,
573 					    pwd->pw_name,
574 					    tuser ? tuser :
575 					    pwd->pw_name,
576 					    bp,
577 					    0,
578 					    "host",
579 					    krb_realm,
580 					    bsd_context,
581 					    &auth_context,
582 					    &cred,
583 					    0,	/* No seq # */
584 					    0,	/* No server seq # */
585 					    authopts,
586 					    0,	/* Not any port # */
587 					    &kcmd_proto);
588 				if (status) {
589 					/*
590 					 * If new protocol requested, we dont
591 					 * fallback to less secure ones.
592 					 */
593 
594 					if (kcmd_proto == KCMD_NEW_PROTOCOL) {
595 						(void) fprintf(stderr,
596 							gettext("rcp: kcmdv2 "
597 							"to host %s failed - %s"
598 							"\nFallback to normal "
599 							"rcp denied."), host,
600 							error_message(status));
601 						exit(1);
602 					}
603 					if (status != -1) {
604 						(void) fprintf(stderr,
605 						gettext("rcp: kcmd to host "
606 						"%s failed - %s,\n"
607 						"trying normal rcp...\n\n"),
608 						host, error_message(status));
609 					} else {
610 						(void) fprintf(stderr,
611 							gettext("trying normal"
612 							" rcp...\n"));
613 					}
614 					/*
615 					 * kcmd() failed, so we have to
616 					 * fallback to normal rcp
617 					 */
618 					try_normal_rcp(prev_argc, prev_argv);
619 				} else {
620 					rem = sock;
621 					session_key = &cred->keyblock;
622 					if (kcmd_proto == KCMD_NEW_PROTOCOL) {
623 						/* CSTYLED */
624 						status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key);
625 						if (status) {
626 							com_err("rcp", status,
627 								"determining "
628 								"subkey for "
629 								"session");
630 							exit(1);
631 						}
632 						if (!session_key) {
633 							com_err("rcp", 0,
634 								"no subkey "
635 								"negotiated for"
636 								" connection");
637 							exit(1);
638 						}
639 					}
640 					eblock.crypto_entry =
641 						session_key->enctype;
642 					eblock.key =
643 						(krb5_keyblock *)session_key;
644 
645 					init_encrypt(encrypt_flag,
646 						bsd_context, kcmd_proto,
647 						&desinbuf, &desoutbuf, CLIENT,
648 						&eblock);
649 					if (encrypt_flag > 0) {
650 						char *s = gettext("This rcp "
651 							"session is using "
652 							"encryption for all "
653 							"data transmissions."
654 							"\r\n");
655 
656 						(void) write(2, s, strlen(s));
657 					}
658 				}
659 				if (response() < 0)
660 					exit(1);
661 
662 				} else {
663 
664 				/*
665 				 * ACL support: try to find out if the remote
666 				 * site is running acl cognizant version of
667 				 * rcp. A special binary name is used for this
668 				 * purpose.
669 				 */
670 				aclflag = 1;
671 				acl_aclflag = 1;
672 
673 				/*
674 				 * First see if the remote side will support
675 				 * both aclent_t and ace_t acl's?
676 				 */
677 				(void) snprintf(bp, buffersize, "%s -tZ %s",
678 							cmd_sunw, targ);
679 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
680 					    tuser ? tuser : pwd->pw_name,
681 					    bp, 0, AF_INET6);
682 				if (rem < 0)
683 					exit(1);
684 
685 				/*
686 				 * This is similar to routine response().
687 				 * If response is not ok, treat the other
688 				 * side as non-acl rcp.
689 				 */
690 				if (read(rem, &resp, sizeof (resp))
691 				    != sizeof (resp))
692 					lostconn();
693 				if (resp != 0) {
694 					acl_aclflag = 0;
695 					(void) snprintf(bp, buffersize,
696 					    "%s -t %s", cmd_sunw, targ);
697 
698 					(void) close(rem);
699 					host = thost;
700 					rem = rcmd_af(&host, portnumber,
701 					    pwd->pw_name,
702 					    tuser ? tuser : pwd->pw_name,
703 					    bp, 0, AF_INET6);
704 					if (rem < 0)
705 						exit(1);
706 
707 					if (read(rem, &resp, sizeof (resp))
708 					    != sizeof (resp))
709 						lostconn();
710 					if (resp != 0) {
711 						/*
712 						 * Not OK:
713 						 * The other side is running
714 						 * non-acl rcp. Try again with
715 						 * normal stuff
716 						 */
717 						aclflag = 0;
718 						(void) snprintf(bp, buffersize,
719 						    "%s -t %s", cmd, targ);
720 						(void) close(rem);
721 						host = thost;
722 						rem = rcmd_af(&host, portnumber,
723 						    pwd->pw_name,
724 						    tuser ? tuser :
725 						    pwd->pw_name, bp, 0,
726 						    AF_INET6);
727 						if (rem < 0)
728 							exit(1);
729 						if (response() < 0)
730 						    exit(1);
731 					}
732 				}
733 				/* everything should be fine now */
734 				(void) setuid(userid);
735 
736 				}
737 			}
738 			source(1, argv + i);
739 		}
740 	}
741 }
742 
743 static void
744 tolocal(int argc, char *argv[])
745 {
746 	int i;
747 	char *host, *src, *suser, *lhost;
748 	char resp;
749 	size_t buffersize;
750 	char bp[RCP_BUFSIZE];
751 	krb5_creds *cred;
752 	char *arglist[MAXARGS+1];
753 	buffersize = RCP_BUFSIZE;
754 
755 	for (i = 0; i < argc - 1; i++) {
756 		if (!(src = colon(argv[i]))) {	/* local to local */
757 			(void) addargs(arglist, "cp",
758 			    iamrecursive ? "-r" : "", pflag ? "-p" : "",
759 			    zflag ? "-z" : "", argv[i], argv[argc - 1],
760 			    (char *)NULL);
761 			if (susystem(_PATH_CP, arglist) == -1)
762 				errs++;
763 			continue;
764 		}
765 		*src++ = 0;
766 		if (*src == 0)
767 			src = ".";
768 		host = search_char((unsigned char *)argv[i], '@');
769 		if (host) {
770 			*host++ = 0;
771 			suser = argv[i];
772 			if (*suser == '\0') {
773 				suser = pwd->pw_name;
774 			} else if (!okname(suser)) {
775 				errs++;
776 				continue;
777 			}
778 		} else {
779 			host = argv[i];
780 			suser = pwd->pw_name;
781 		}
782 		host = removebrackets(host);
783 		lhost = host;
784 		if (krb5auth_flag > 0) {
785 
786 		(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
787 		authopts = AP_OPTS_MUTUAL_REQUIRED;
788 		status = kcmd(&sock, &host,
789 				portnumber,
790 				pwd->pw_name, suser,
791 				bp,
792 				0,	/* &rfd2 */
793 				"host",
794 				krb_realm,
795 				bsd_context,
796 				&auth_context,
797 				&cred,
798 				0,	/* No seq # */
799 				0,	/* No server seq # */
800 				authopts,
801 				1,	/* Not any port # */
802 				&kcmd_proto);
803 		if (status) {
804 			/*
805 			 * If new protocol requested, we dont
806 			 * fallback to less secure ones.
807 			 */
808 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
809 				(void) fprintf(stderr, gettext("rcp: kcmdv2 "
810 					"to host %s failed - %s\n"
811 					"Fallback to normal rcp denied."),
812 					host, error_message(status));
813 				exit(1);
814 			}
815 			if (status != -1) {
816 				(void) fprintf(stderr, gettext("rcp: kcmd "
817 						"to host %s failed - %s,\n"
818 						"trying normal rcp...\n\n"),
819 						host, error_message(status));
820 			} else {
821 				(void) fprintf(stderr,
822 					gettext("trying normal rcp...\n"));
823 			}
824 			/*
825 			 * kcmd() failed, so we have to
826 			 * fallback to normal rcp
827 			 */
828 			try_normal_rcp(prev_argc, prev_argv);
829 		} else {
830 			rem = sock;
831 			session_key = &cred->keyblock;
832 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
833 				status = krb5_auth_con_getlocalsubkey(
834 						bsd_context, auth_context,
835 						&session_key);
836 				if (status) {
837 					com_err("rcp", status, "determining "
838 						"subkey for session");
839 					exit(1);
840 				}
841 				if (!session_key) {
842 					com_err("rcp", 0, "no subkey negotiated"
843 						" for connection");
844 					exit(1);
845 				}
846 			}
847 			eblock.crypto_entry = session_key->enctype;
848 			eblock.key = (krb5_keyblock *)session_key;
849 
850 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
851 					&desinbuf, &desoutbuf, CLIENT,
852 					&eblock);
853 			if (encrypt_flag > 0) {
854 				char *s = gettext("This rcp "
855 					"session is using DES "
856 					"encryption for all "
857 					"data transmissions."
858 					"\r\n");
859 
860 				(void) write(2, s, strlen(s));
861 			}
862 		}
863 
864 		}
865 		else
866 		{
867 
868 		/*
869 		 * ACL support: try to find out if the remote site is
870 		 * running acl cognizant version of rcp.
871 		 */
872 		aclflag = 1;
873 		acl_aclflag = 1;
874 
875 		(void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, src);
876 		rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
877 			    bp, 0, AF_INET6);
878 
879 		if (rem < 0) {
880 			++errs;
881 			continue;
882 		}
883 
884 		/*
885 		 * The remote system is supposed to send an ok response.
886 		 * If there are any data other than "ok", it must be error
887 		 * messages from the remote system. We can assume the
888 		 * remote system is running non-acl version rcp.
889 		 */
890 		if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
891 			lostconn();
892 		if (resp != 0) {
893 
894 			/*
895 			 * Try again without ace_acl support
896 			 */
897 			acl_aclflag = 0;
898 			(void) snprintf(bp, buffersize, "%s -f %s",
899 			    cmd_sunw, src);
900 			rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
901 			    bp, 0, AF_INET6);
902 
903 			if (rem < 0) {
904 				++errs;
905 				continue;
906 			}
907 
908 			if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
909 				lostconn();
910 
911 			/*
912 			 * NOT ok:
913 			 * The other side is running non-acl rcp.
914 			 * Try again with normal stuff
915 			 */
916 			aclflag = 0;
917 			(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
918 				(void) close(rem);
919 				host = lhost;
920 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
921 						suser, bp, 0, AF_INET6);
922 			if (rem < 0) {
923 				++errs;
924 				continue;
925 			}
926 		}
927 		}
928 
929 		sink(1, argv + argc - 1);
930 
931 		(void) close(rem);
932 		rem = -1;
933 	}
934 }
935 
936 
937 static void
938 verifydir(char *cp)
939 {
940 	struct stat stb;
941 
942 	if (stat(cp, &stb) >= 0) {
943 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
944 			return;
945 		errno = ENOTDIR;
946 	}
947 	error("rcp: %s: %s.\n", cp, strerror(errno));
948 	exit(1);
949 }
950 
951 static char *
952 colon(char *cp)
953 {
954 	boolean_t is_bracket_open = B_FALSE;
955 
956 	for (; *cp; ++cp) {
957 		if (*cp == '[')
958 			is_bracket_open = B_TRUE;
959 		else if (*cp == ']')
960 			is_bracket_open = B_FALSE;
961 		else if (*cp == ':' && !is_bracket_open)
962 			return (cp);
963 		else if (*cp == '/')
964 			return (0);
965 	}
966 	return (0);
967 }
968 
969 static int
970 okname(char *cp0)
971 {
972 	register char *cp = cp0;
973 	register int c;
974 
975 	do {
976 		c = *cp;
977 		if (c & 0200)
978 			goto bad;
979 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
980 			goto bad;
981 	} while (*++cp);
982 	return (1);
983 bad:
984 	(void) fprintf(stderr, "rcp: invalid user name %s\n", cp0);
985 	return (0);
986 }
987 
988 
989 static char *
990 removebrackets(char *str)
991 {
992 	char *newstr = str;
993 
994 	if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
995 		newstr = str + 1;
996 		str[strlen(str) - 1] = '\0';
997 	}
998 	return (newstr);
999 }
1000 
1001 static int
1002 susystem(char *path, char **arglist)
1003 {
1004 	int status, pid, w;
1005 	register void (*istat)(), (*qstat)();
1006 	int pfds[2];
1007 	char buf[BUFSIZ];
1008 	int cnt;
1009 	boolean_t seen_stderr_traffic;
1010 
1011 	/*
1012 	 * Due to the fact that rcp uses rsh to copy between 2 remote
1013 	 * machines, rsh doesn't return the exit status of the remote
1014 	 * command, and we can't modify the rcmd protocol used by rsh
1015 	 * (for interoperability reasons) we use the hack of using any
1016 	 * output on stderr as indication that an error occurred and
1017 	 * that we should return a non-zero error code.
1018 	 */
1019 
1020 	if (pipe(pfds) == -1) {
1021 		(void) fprintf(stderr, "Couldn't create pipe: %s\n",
1022 		    strerror(errno));
1023 		return (-1);
1024 	}
1025 
1026 	if ((pid = vfork()) < 0) {
1027 		(void) close(pfds[0]);
1028 		(void) close(pfds[1]);
1029 		(void) fprintf(stderr, "Couldn't fork child process: %s\n",
1030 		    strerror(errno));
1031 		return (-1);
1032 	} else if (pid == 0) {
1033 		/*
1034 		 * Child.
1035 		 */
1036 		(void) close(pfds[0]);
1037 		/*
1038 		 * Send stderr messages down the pipe so that we can detect
1039 		 * them in the parent process.
1040 		 */
1041 		if (pfds[1] != STDERR_FILENO) {
1042 			(void) dup2(pfds[1], STDERR_FILENO);
1043 			(void) close(pfds[1]);
1044 		}
1045 		/*
1046 		 * This shell does not inherit the additional privilege
1047 		 * we have in our Permitted set.
1048 		 */
1049 		(void) execv(path, arglist);
1050 		_exit(127);
1051 	}
1052 	/*
1053 	 * Parent.
1054 	 */
1055 	istat = signal(SIGINT, SIG_IGN);
1056 	qstat = signal(SIGQUIT, SIG_IGN);
1057 
1058 	(void) close(pfds[1]);
1059 	seen_stderr_traffic = B_FALSE;
1060 	while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) {
1061 		/*
1062 		 * If any data is read from the pipe the child process
1063 		 * has output something on stderr so we set the boolean
1064 		 * 'seen_stderr_traffic' to true, which will cause the
1065 		 * function to return -1.
1066 		 */
1067 		(void) write(STDERR_FILENO, buf, cnt);
1068 		seen_stderr_traffic = B_TRUE;
1069 	}
1070 	(void) close(pfds[0]);
1071 	while ((w = wait(&status)) != pid && w != -1)
1072 		;
1073 	if (w == -1)
1074 		status = -1;
1075 
1076 	(void) signal(SIGINT, istat);
1077 	(void) signal(SIGQUIT, qstat);
1078 
1079 	return (seen_stderr_traffic ? -1 : status);
1080 }
1081 
1082 static void
1083 source(int argc, char *argv[])
1084 {
1085 	struct stat stb;
1086 	static BUF buffer;
1087 	BUF *bp;
1088 	int x, readerr, f, amt;
1089 	char *last, *name, buf[RCP_BUFSIZE];
1090 	off_t off, size, i;
1091 	ssize_t cnt;
1092 	struct linger lingerbuf;
1093 
1094 	for (x = 0; x < argc; x++) {
1095 		name = argv[x];
1096 		if ((f = open(name, O_RDONLY, 0)) < 0) {
1097 			error("rcp: %s: %s\n", name, strerror(errno));
1098 			continue;
1099 		}
1100 		if (fstat(f, &stb) < 0)
1101 			goto notreg;
1102 		switch (stb.st_mode&S_IFMT) {
1103 
1104 		case S_IFREG:
1105 			break;
1106 
1107 		case S_IFDIR:
1108 			if (iamrecursive) {
1109 				(void) close(f);
1110 				rsource(name, &stb);
1111 				continue;
1112 			}
1113 			/* FALLTHROUGH */
1114 		default:
1115 notreg:
1116 			(void) close(f);
1117 			error("rcp: %s: not a plain file\n", name);
1118 			continue;
1119 		}
1120 		last = rindex(name, '/');
1121 		if (last == 0)
1122 			last = name;
1123 		else
1124 			last++;
1125 		if (pflag) {
1126 			time_t mtime, atime;
1127 			time_t now;
1128 
1129 			/*
1130 			 * Make it compatible with possible future
1131 			 * versions expecting microseconds.
1132 			 */
1133 			mtime = stb.st_mtime;
1134 			atime = stb.st_atime;
1135 
1136 			if ((mtime < 0) || (atime < 0)) {
1137 				now = time(NULL);
1138 
1139 				if (mtime < 0) {
1140 					mtime = now;
1141 					error("negative modification time on "
1142 					    "%s; not preserving\n", name);
1143 				}
1144 				if (atime < 0) {
1145 					atime = now;
1146 					error("negative access time on "
1147 					    "%s; not preserving\n", name);
1148 				}
1149 			}
1150 			(void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n",
1151 							mtime, atime);
1152 			(void) desrcpwrite(rem, buf, strlen(buf));
1153 			if (response() < 0) {
1154 				(void) close(f);
1155 				continue;
1156 			}
1157 		}
1158 		(void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
1159 			(uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size,
1160 			last);
1161 		(void) desrcpwrite(rem, buf, strlen(buf));
1162 		if (response() < 0) {
1163 			(void) close(f);
1164 			continue;
1165 		}
1166 
1167 		/* ACL support: send */
1168 		if (aclflag | acl_aclflag) {
1169 			/* get acl from f and send it over */
1170 			if (sendacl(f) == ACL_FAIL) {
1171 				(void) close(f);
1172 				continue;
1173 			}
1174 		}
1175 		if ((krb5auth_flag > 0) || (iamremote == 1)) {
1176 			bp = allocbuf(&buffer, f, RCP_BUFSIZE);
1177 			if (bp == NULLBUF) {
1178 				(void) close(f);
1179 				continue;
1180 			}
1181 			readerr = 0;
1182 			for (i = 0; i < stb.st_size; i += bp->cnt) {
1183 				amt = bp->cnt;
1184 				if (i + amt > stb.st_size)
1185 					amt = stb.st_size - i;
1186 				if (readerr == 0 &&
1187 				    read(f, bp->buf, amt) != amt)
1188 					readerr = errno;
1189 				(void) desrcpwrite(rem, bp->buf, amt);
1190 			}
1191 			(void) close(f);
1192 			if (readerr == 0)
1193 				(void) desrcpwrite(rem, "", 1);
1194 			else
1195 				error("rcp: %s: %s\n", name,
1196 				    error_message(readerr));
1197 		} else {
1198 			cnt = off = 0;
1199 			size = stb.st_size;
1200 			while (size != 0) {
1201 				amt = MIN(size, SENDFILE_SIZE);
1202 				cnt = sendfile(rem, f, &off, amt);
1203 				if (cnt == -1) {
1204 					if (errno == EINTR) {
1205 						continue;
1206 					} else {
1207 						break;
1208 					}
1209 				}
1210 				if (cnt == 0)
1211 					break;
1212 				size -= cnt;
1213 			}
1214 			if (cnt < 0) {
1215 				error("rcp: %s: %s\n", name, strerror(errno));
1216 			} else if (cnt == 0 && size != 0) {
1217 				error("rcp: %s: unexpected end of file\n",
1218 					name);
1219 				lingerbuf.l_onoff = 1;
1220 				lingerbuf.l_linger = 0;
1221 				(void) setsockopt(rem, SOL_SOCKET, SO_LINGER,
1222 					&lingerbuf, sizeof (lingerbuf));
1223 				/*
1224 				 * When response() (see below) is invoked it
1225 				 * tries to read data from closed handle which
1226 				 * triggers error and lostconn() function.
1227 				 * lostconn() terminates the program with
1228 				 * appropriate message.
1229 				 */
1230 				(void) close(rem);
1231 				rem = -1;
1232 			} else {
1233 				(void) write(rem, "", 1);
1234 			}
1235 			(void) close(f);
1236 		}
1237 		(void) response();
1238 	}
1239 }
1240 
1241 
1242 static void
1243 rsource(char *name, struct stat *statp)
1244 {
1245 	DIR *d;
1246 	struct dirent *dp;
1247 	char *last, *vect[1];
1248 	char path[MAXPATHLEN];
1249 
1250 	if (!(d = opendir(name))) {
1251 		error("rcp: %s: %s\n", name, strerror(errno));
1252 		return;
1253 	}
1254 	last = rindex(name, '/');
1255 	if (last == 0)
1256 		last = name;
1257 	else
1258 		last++;
1259 	if (pflag) {
1260 		(void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n",
1261 				statp->st_mtime, statp->st_atime);
1262 		(void) desrcpwrite(rem, path, strlen(path));
1263 		if (response() < 0) {
1264 			(void) closedir(d);
1265 			return;
1266 		}
1267 	}
1268 	(void) snprintf(path, sizeof (path), "D%04o %d %s\n",
1269 	    (uint_t)(statp->st_mode & 07777), 0, last);
1270 	(void) desrcpwrite(rem, path, strlen(path));
1271 
1272 	/* acl support for directory */
1273 	if (aclflag) {
1274 		/* get acl from f and send it over */
1275 		if (sendacl(d->dd_fd) == ACL_FAIL) {
1276 			(void) closedir(d);
1277 			return;
1278 		}
1279 	}
1280 
1281 	if (response() < 0) {
1282 		(void) closedir(d);
1283 		return;
1284 	}
1285 
1286 	while (dp = readdir(d)) {
1287 		if (dp->d_ino == 0)
1288 			continue;
1289 		if ((strcmp(dp->d_name, ".") == 0) ||
1290 		    (strcmp(dp->d_name, "..") == 0))
1291 			continue;
1292 		if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >=
1293 			MAXPATHLEN - 1) {
1294 			error("%s/%s: name too long.\n", name, dp->d_name);
1295 			continue;
1296 		}
1297 		(void) snprintf(path, sizeof (path), "%s/%s",
1298 					name, dp->d_name);
1299 		vect[0] = path;
1300 		source(1, vect);
1301 	}
1302 	(void) closedir(d);
1303 	(void) desrcpwrite(rem, "E\n", 2);
1304 	(void) response();
1305 }
1306 
1307 static int
1308 response(void)
1309 {
1310 	register char *cp;
1311 	char ch, resp, rbuf[RCP_BUFSIZE];
1312 
1313 	if (desrcpread(rem, &resp, 1) != 1)
1314 		lostconn();
1315 	cp = rbuf;
1316 	switch (resp) {
1317 	case 0:				/* ok */
1318 		return (0);
1319 	default:
1320 		*cp++ = resp;
1321 		/* FALLTHROUGH */
1322 	case 1:				/* error, followed by err msg */
1323 	case 2:				/* fatal error, "" */
1324 		do {
1325 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1326 				lostconn();
1327 			*cp++ = ch;
1328 		} while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n');
1329 
1330 		if (!iamremote)
1331 			(void) write(STDERR_FILENO, rbuf, cp - rbuf);
1332 		++errs;
1333 		if (resp == 1)
1334 			return (-1);
1335 		exit(1);
1336 	}
1337 	/*NOTREACHED*/
1338 }
1339 
1340 static void
1341 lostconn(void)
1342 {
1343 	if (!iamremote)
1344 		(void) fprintf(stderr, "rcp: lost connection\n");
1345 	exit(1);
1346 }
1347 
1348 
1349 static void
1350 sink(int argc, char *argv[])
1351 {
1352 	char *cp;
1353 	static BUF buffer;
1354 	struct stat stb;
1355 	struct timeval tv[2];
1356 	BUF *bp;
1357 	off_t i, j;
1358 	char ch, *targ, *why;
1359 	int amt, count, exists, first, mask, mode;
1360 	off_t size;
1361 	int ofd, setimes, targisdir, wrerr;
1362 	char *np, *vect[1], buf[RCP_BUFSIZE];
1363 	char *namebuf = NULL;
1364 	size_t namebuf_sz = 0;
1365 	size_t need;
1366 
1367 #define	atime	tv[0]
1368 #define	mtime	tv[1]
1369 #define	SCREWUP(str)	{ why = str; goto screwup; }
1370 
1371 	setimes = targisdir = 0;
1372 	mask = umask(0);
1373 	if (!pflag)
1374 		(void) umask(mask);
1375 	if (argc != 1) {
1376 		error("rcp: ambiguous target\n");
1377 		exit(1);
1378 	}
1379 	targ = *argv;
1380 	if (targetshouldbedirectory)
1381 		verifydir(targ);
1382 	(void) desrcpwrite(rem, "", 1);
1383 
1384 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
1385 		targisdir = 1;
1386 	for (first = 1; ; first = 0) {
1387 		cp = buf;
1388 		if (desrcpread(rem, cp, 1) <= 0) {
1389 			if (namebuf != NULL)
1390 				free(namebuf);
1391 			return;
1392 		}
1393 
1394 		if (*cp++ == '\n')
1395 			SCREWUP("unexpected <newline>");
1396 		do {
1397 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1398 				SCREWUP("lost connection");
1399 			*cp++ = ch;
1400 		} while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n');
1401 		*cp = 0;
1402 
1403 		if (buf[0] == '\01' || buf[0] == '\02') {
1404 			if (iamremote == 0)
1405 				(void) write(STDERR_FILENO, buf + 1,
1406 				    strlen(buf + 1));
1407 			if (buf[0] == '\02')
1408 				exit(1);
1409 			errs++;
1410 			continue;
1411 		}
1412 		if (buf[0] == 'E') {
1413 			(void) desrcpwrite(rem, "", 1);
1414 			if (namebuf != NULL)
1415 				free(namebuf);
1416 			return;
1417 		}
1418 
1419 		if (ch == '\n')
1420 			*--cp = 0;
1421 		cp = buf;
1422 		if (*cp == 'T') {
1423 			setimes++;
1424 			cp++;
1425 			mtime.tv_sec = strtol(cp, &cp, 0);
1426 			if (*cp++ != ' ')
1427 				SCREWUP("mtime.sec not delimited");
1428 			mtime.tv_usec = strtol(cp, &cp, 0);
1429 			if (*cp++ != ' ')
1430 				SCREWUP("mtime.usec not delimited");
1431 			atime.tv_sec = strtol(cp, &cp, 0);
1432 			if (*cp++ != ' ')
1433 				SCREWUP("atime.sec not delimited");
1434 			atime.tv_usec = strtol(cp, &cp, 0);
1435 			if (*cp++ != '\0')
1436 				SCREWUP("atime.usec not delimited");
1437 			(void) desrcpwrite(rem, "", 1);
1438 			continue;
1439 		}
1440 		if (*cp != 'C' && *cp != 'D') {
1441 			/*
1442 			 * Check for the case "rcp remote:foo\* local:bar".
1443 			 * In this case, the line "No match." can be returned
1444 			 * by the shell before the rcp command on the remote is
1445 			 * executed so the ^Aerror_message convention isn't
1446 			 * followed.
1447 			 */
1448 			if (first) {
1449 				error("%s\n", cp);
1450 				exit(1);
1451 			}
1452 			SCREWUP("expected control record");
1453 		}
1454 		mode = 0;
1455 		for (++cp; cp < buf + 5; cp++) {
1456 			if (*cp < '0' || *cp > '7')
1457 				SCREWUP("bad mode");
1458 			mode = (mode << 3) | (*cp - '0');
1459 		}
1460 		if (*cp++ != ' ')
1461 			SCREWUP("mode not delimited");
1462 		size = 0;
1463 		while (isdigit(*cp))
1464 			size = size * 10 + (*cp++ - '0');
1465 		if (*cp++ != ' ')
1466 			SCREWUP("size not delimited");
1467 		if (targisdir) {
1468 			need = strlen(targ) + sizeof ("/") + strlen(cp);
1469 			if (need > namebuf_sz) {
1470 			    if ((namebuf = realloc(namebuf, need)) == NULL) {
1471 					error("rcp: out of memory\n");
1472 					exit(1);
1473 			    }
1474 			    namebuf_sz = need;
1475 			}
1476 			(void) snprintf(namebuf, need, "%s%s%s", targ,
1477 			    *targ ? "/" : "", cp);
1478 			np = namebuf;
1479 		} else {
1480 			np = targ;
1481 		}
1482 
1483 		exists = stat(np, &stb) == 0;
1484 		if (buf[0] == 'D') {
1485 			if (exists) {
1486 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
1487 					if (aclflag | acl_aclflag) {
1488 						/*
1489 						 * consume acl in the pipe
1490 						 * fd = -1 to indicate the
1491 						 * special case
1492 						 */
1493 						if (recvacl(-1, exists, pflag)
1494 						    == ACL_FAIL) {
1495 							goto bad;
1496 						}
1497 					}
1498 					errno = ENOTDIR;
1499 					goto bad;
1500 				}
1501 				if (pflag)
1502 					(void) chmod(np, mode);
1503 			} else if (mkdir(np, mode) < 0) {
1504 				if (aclflag) {
1505 					/* consume acl in the pipe */
1506 					(void) recvacl(-1, exists, pflag);
1507 				}
1508 				goto bad;
1509 			}
1510 
1511 			/* acl support for directories */
1512 			if (aclflag | acl_aclflag) {
1513 				int dfd;
1514 
1515 				if ((dfd = open(np, O_RDONLY)) == -1)
1516 					goto bad;
1517 
1518 				/* get acl and set it to ofd */
1519 				if (recvacl(dfd, exists, pflag) == ACL_FAIL) {
1520 					(void) close(dfd);
1521 					if (!exists)
1522 						(void) rmdir(np);
1523 					goto bad;
1524 				}
1525 				(void) close(dfd);
1526 			}
1527 
1528 			vect[0] = np;
1529 			sink(1, vect);
1530 			if (setimes) {
1531 				setimes = 0;
1532 				if (utimes(np, tv) < 0)
1533 				    error("rcp: can't set times on %s: %s\n",
1534 					np, strerror(errno));
1535 			}
1536 			continue;
1537 		}
1538 
1539 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
1540 bad:
1541 			error("rcp: %s: %s\n", np, strerror(errno));
1542 			continue;
1543 		}
1544 
1545 		/*
1546 		 * If the output file exists we have to force zflag off
1547 		 * to avoid erroneously seeking past old data.
1548 		 */
1549 		zopen(ofd, zflag && !exists);
1550 
1551 		if (exists && pflag)
1552 			(void) fchmod(ofd, mode);
1553 
1554 		(void) desrcpwrite(rem, "", 1);
1555 
1556 		/*
1557 		 * ACL support: receiving
1558 		 */
1559 		if (aclflag | acl_aclflag) {
1560 			/* get acl and set it to ofd */
1561 			if (recvacl(ofd, exists, pflag) == ACL_FAIL) {
1562 				(void) close(ofd);
1563 				if (!exists)
1564 					(void) unlink(np);
1565 				continue;
1566 			}
1567 		}
1568 
1569 		if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) {
1570 			(void) close(ofd);
1571 			continue;
1572 		}
1573 		cp = bp->buf;
1574 		count = 0;
1575 		wrerr = 0;
1576 		for (i = 0; i < size; i += RCP_BUFSIZE) {
1577 			amt = RCP_BUFSIZE;
1578 			if (i + amt > size)
1579 				amt = size - i;
1580 			count += amt;
1581 			do {
1582 				j = desrcpread(rem, cp, amt);
1583 				if (j <= 0) {
1584 					int sverrno = errno;
1585 
1586 					/*
1587 					 * Connection to supplier lost.
1588 					 * Truncate file to correspond
1589 					 * to amount already transferred.
1590 					 *
1591 					 * Note that we must call ftruncate()
1592 					 * before any call to error() (which
1593 					 * might result in a SIGPIPE and
1594 					 * sudden death before we have a chance
1595 					 * to correct the file's size).
1596 					 */
1597 					size = lseek(ofd, 0, SEEK_CUR);
1598 					if ((ftruncate(ofd, size)  == -1) &&
1599 					    (errno != EINVAL) &&
1600 					    (errno != EACCES))
1601 #define		TRUNCERR	"rcp: can't truncate %s: %s\n"
1602 						error(TRUNCERR, np,
1603 						    strerror(errno));
1604 					error("rcp: %s\n",
1605 					    j ? strerror(sverrno) :
1606 					    "dropped connection");
1607 					(void) close(ofd);
1608 					exit(1);
1609 				}
1610 				amt -= j;
1611 				cp += j;
1612 			} while (amt > 0);
1613 			if (count == bp->cnt) {
1614 				cp = bp->buf;
1615 				if (wrerr == 0 &&
1616 				    zwrite(ofd, cp, count) < 0)
1617 					wrerr++;
1618 				count = 0;
1619 			}
1620 		}
1621 		if (count != 0 && wrerr == 0 &&
1622 		    zwrite(ofd, bp->buf, count) < 0)
1623 			wrerr++;
1624 		if (zclose(ofd) < 0)
1625 			wrerr++;
1626 
1627 
1628 		if ((ftruncate(ofd, size)  == -1) && (errno != EINVAL) &&
1629 		    (errno != EACCES)) {
1630 			error(TRUNCERR, np, strerror(errno));
1631 		}
1632 		(void) close(ofd);
1633 		(void) response();
1634 		if (setimes) {
1635 			setimes = 0;
1636 			if (utimes(np, tv) < 0)
1637 				error("rcp: can't set times on %s: %s\n",
1638 				    np, strerror(errno));
1639 		}
1640 		if (wrerr)
1641 			error("rcp: %s: %s\n", np, strerror(errno));
1642 		else
1643 			(void) desrcpwrite(rem, "", 1);
1644 	}
1645 screwup:
1646 	error("rcp: protocol screwup: %s\n", why);
1647 	exit(1);
1648 }
1649 
1650 #ifndef roundup
1651 #define	roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
1652 #endif /* !roundup */
1653 
1654 static BUF *
1655 allocbuf(BUF *bp, int fd, int blksize)
1656 {
1657 	struct stat stb;
1658 	int size;
1659 
1660 	if (fstat(fd, &stb) < 0) {
1661 		error("rcp: fstat: %s\n", strerror(errno));
1662 		return (0);
1663 	}
1664 	size = roundup(stb.st_blksize, blksize);
1665 	if (size == 0)
1666 		size = blksize;
1667 	if (bp->cnt < size) {
1668 		if (bp->buf != 0)
1669 			free(bp->buf);
1670 		bp->buf = (char *)malloc((uint_t)size);
1671 		if (!bp->buf) {
1672 			error("rcp: malloc: out of memory\n");
1673 			return (0);
1674 		}
1675 	}
1676 	bp->cnt = size;
1677 	return (bp);
1678 }
1679 
1680 static void
1681 usage(void)
1682 {
1683 	(void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"),
1684 		gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
1685 #ifdef DEBUG
1686 			"[-D port] "
1687 #endif /* DEBUG */
1688 			"f1 f2; or:\n"),
1689 		gettext("\trcp [-r] [-p] [-a] [-x] "
1690 #ifdef DEBUG
1691 			"[-D port] "
1692 #endif /* DEBUG */
1693 			"[-k realm] [-PN / -PO] f1...fn d2\n"));
1694 	exit(1);
1695 }
1696 
1697 
1698 /*
1699  * sparse file support
1700  */
1701 
1702 static off_t zbsize;
1703 static off_t zlastseek;
1704 
1705 /* is it ok to try to create holes? */
1706 static void
1707 zopen(int fd, int flag)
1708 {
1709 	struct stat st;
1710 
1711 	zbsize = 0;
1712 	zlastseek = 0;
1713 
1714 	if (flag &&
1715 		fstat(fd, &st) == 0 &&
1716 		(st.st_mode & S_IFMT) == S_IFREG)
1717 		zbsize = st.st_blksize;
1718 }
1719 
1720 /* write and/or seek */
1721 static int
1722 zwrite(int fd, char *buf, int nbytes)
1723 {
1724 	off_t block = zbsize ? zbsize : nbytes;
1725 
1726 	do {
1727 		if (block > nbytes)
1728 			block = nbytes;
1729 		nbytes -= block;
1730 
1731 		if (!zbsize || notzero(buf, block)) {
1732 			register int n, count = block;
1733 
1734 			do {
1735 				if ((n = write(fd, buf, count)) < 0)
1736 					return (-1);
1737 				buf += n;
1738 			} while ((count -= n) > 0);
1739 			zlastseek = 0;
1740 		} else {
1741 			if (lseek(fd, (off_t)block, SEEK_CUR) < 0)
1742 				return (-1);
1743 			buf += block;
1744 			zlastseek = 1;
1745 		}
1746 	} while (nbytes > 0);
1747 
1748 	return (0);
1749 }
1750 
1751 /* write last byte of file if necessary */
1752 static int
1753 zclose(int fd)
1754 {
1755 	zbsize = 0;
1756 
1757 	if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 ||
1758 		zwrite(fd, "", 1) < 0))
1759 		return (-1);
1760 	else
1761 		return (0);
1762 }
1763 
1764 /* return true if buffer is not all zeros */
1765 static int
1766 notzero(char *p, int n)
1767 {
1768 	register int result = 0;
1769 
1770 	while ((int)p & 3 && --n >= 0)
1771 		result |= *p++;
1772 
1773 	while ((n -= 4 * sizeof (int)) >= 0) {
1774 		/* LINTED */
1775 		result |= ((int *)p)[0];
1776 		/* LINTED */
1777 		result |= ((int *)p)[1];
1778 		/* LINTED */
1779 		result |= ((int *)p)[2];
1780 		/* LINTED */
1781 		result |= ((int *)p)[3];
1782 		if (result)
1783 			return (result);
1784 		p += 4 * sizeof (int);
1785 	}
1786 	n += 4 * sizeof (int);
1787 
1788 	while (--n >= 0)
1789 		result |= *p++;
1790 
1791 	return (result);
1792 }
1793 
1794 /*
1795  * New functions to support ACLs
1796  */
1797 
1798 /*
1799  * Get acl from f and send it over.
1800  * ACL record includes acl entry count, acl text length, and acl text.
1801  */
1802 static int
1803 sendacl(int f)
1804 {
1805 	int		aclcnt;
1806 	char		*acltext;
1807 	char		buf[BUFSIZ];
1808 	acl_t		*aclp;
1809 	char		acltype;
1810 	int		aclerror;
1811 	int		trivial;
1812 
1813 
1814 	aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp);
1815 	if (aclerror != 0) {
1816 		error("can't retrieve ACL: %s \n", acl_strerror(aclerror));
1817 		return (ACL_FAIL);
1818 	}
1819 
1820 	/*
1821 	 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0
1822 	 * then don't do the malloc and facl(fd, getcntcmd,...);
1823 	 * since the remote side doesn't support alternate style ACL's.
1824 	 */
1825 
1826 	if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) {
1827 		aclcnt = MIN_ACL_ENTRIES;
1828 		acltype = 'A';
1829 		trivial = ACL_IS_TRIVIAL;
1830 	} else {
1831 
1832 		aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0;
1833 
1834 		if (aclp) {
1835 			acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A';
1836 			aclcnt = acl_cnt(aclp);
1837 			trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL);
1838 		} else {
1839 			acltype = 'A';
1840 			aclcnt = MIN_ACL_ENTRIES;
1841 			trivial = ACL_IS_TRIVIAL;
1842 		}
1843 
1844 	}
1845 
1846 	/* send the acl count over */
1847 	(void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt);
1848 	(void) desrcpwrite(rem, buf, strlen(buf));
1849 
1850 	/*
1851 	 * only send acl when we have an aclp, which would
1852 	 * imply its not trivial.
1853 	 */
1854 	if (aclp && (trivial != ACL_IS_TRIVIAL)) {
1855 		acltext = acl_totext(aclp, 0);
1856 		if (acltext == NULL) {
1857 			error("rcp: failed to convert to text\n");
1858 			acl_free(aclp);
1859 			return (ACL_FAIL);
1860 		}
1861 
1862 		/* send ACLs over: send the length first */
1863 		(void) snprintf(buf, sizeof (buf), "%c%d\n",
1864 		    acltype, strlen(acltext));
1865 
1866 		(void) desrcpwrite(rem, buf, strlen(buf));
1867 		(void) desrcpwrite(rem, acltext, strlen(acltext));
1868 		free(acltext);
1869 		if (response() < 0) {
1870 			acl_free(aclp);
1871 			return (ACL_FAIL);
1872 		}
1873 
1874 	}
1875 
1876 	if (aclp)
1877 		acl_free(aclp);
1878 	return (ACL_OK);
1879 }
1880 
1881 /*
1882  * Use this routine to get acl entry count and acl text size (in bytes)
1883  */
1884 static int
1885 getaclinfo(int *cnt, int *acltype)
1886 {
1887 	char		buf[BUFSIZ];
1888 	char		*cp;
1889 	char		ch;
1890 
1891 	/* get acl count */
1892 	cp = buf;
1893 	if (desrcpread(rem, cp, 1) <= 0)
1894 		return (ACL_FAIL);
1895 
1896 	switch (*cp++) {
1897 	case 'A':
1898 		*acltype = 0;
1899 		break;
1900 	case 'Z':
1901 		*acltype = 1;
1902 		break;
1903 	default:
1904 		error("rcp: expect an ACL record, but got %c\n", *cp);
1905 		return (ACL_FAIL);
1906 	}
1907 	do {
1908 		if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) {
1909 			error("rcp: lost connection ..\n");
1910 			return (ACL_FAIL);
1911 		}
1912 		*cp++ = ch;
1913 	} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
1914 	if (ch != '\n') {
1915 		error("rcp: ACL record corrupted \n");
1916 		return (ACL_FAIL);
1917 	}
1918 	cp = &buf[1];
1919 	*cnt = strtol(cp, &cp, 0);
1920 	if (*cp != '\n') {
1921 		error("rcp: ACL record corrupted \n");
1922 		return (ACL_FAIL);
1923 	}
1924 	return (ACL_OK);
1925 }
1926 
1927 
1928 /*
1929  * Receive acl from the pipe and set it to f
1930  */
1931 static int
1932 recvacl(int f, int exists, int preserve)
1933 {
1934 	int		aclcnt;		/* acl entry count */
1935 	int		aclsize;	/* acl text length */
1936 	int		j;
1937 	char		*tp;
1938 	char		*acltext;	/* external format */
1939 	acl_t		*aclp;
1940 	int		acltype;
1941 	int		min_entries;
1942 	int		aclerror;
1943 
1944 	/* get acl count */
1945 	if (getaclinfo(&aclcnt, &acltype) != ACL_OK)
1946 		return (ACL_FAIL);
1947 
1948 	if (acltype == 0) {
1949 		min_entries = MIN_ACL_ENTRIES;
1950 	} else {
1951 		min_entries = 1;
1952 	}
1953 
1954 	if (aclcnt > min_entries) {
1955 		/* get acl text size */
1956 		if (getaclinfo(&aclsize, &acltype) != ACL_OK)
1957 			return (ACL_FAIL);
1958 		if ((acltext = malloc(aclsize + 1)) == NULL) {
1959 			error("rcp: cant allocate memory: %d\n", aclsize);
1960 			return (ACL_FAIL);
1961 		}
1962 
1963 		tp = acltext;
1964 		do {
1965 			j = desrcpread(rem, tp, aclsize);
1966 			if (j <= 0) {
1967 				error("rcp: %s\n", j ? strerror(errno) :
1968 				    "dropped connection");
1969 				exit(1);
1970 			}
1971 			aclsize -= j;
1972 			tp += j;
1973 		} while (aclsize > 0);
1974 		*tp = '\0';
1975 
1976 		if (preserve || !exists) {
1977 			aclerror = acl_fromtext(acltext, &aclp);
1978 			if (aclerror != 0) {
1979 				error("rcp: failed to parse acl : %s\n",
1980 				    acl_strerror(aclerror));
1981 				free(acltext);
1982 				return (ACL_FAIL);
1983 			}
1984 
1985 			if (f != -1) {
1986 				if (facl_set(f, aclp) < 0) {
1987 					error("rcp: failed to set acl\n");
1988 					acl_free(aclp);
1989 					free(acltext);
1990 					return (ACL_FAIL);
1991 				}
1992 			}
1993 			/* -1 means that just consume the data in the pipe */
1994 			acl_free(aclp);
1995 		}
1996 		free(acltext);
1997 		(void) desrcpwrite(rem, "", 1);
1998 	}
1999 	return (ACL_OK);
2000 }
2001 
2002 
2003 static char *
2004 search_char(unsigned char *cp, unsigned char chr)
2005 {
2006 	int	len;
2007 
2008 	while (*cp) {
2009 		if (*cp == chr)
2010 			return ((char *)cp);
2011 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
2012 			len = 1;
2013 		cp += len;
2014 	}
2015 	return (0);
2016 }
2017 
2018 
2019 static int
2020 desrcpread(int fd, char *buf, int len)
2021 {
2022 	return ((int)desread(fd, buf, len, 0));
2023 }
2024 
2025 static int
2026 desrcpwrite(int fd, char *buf, int len)
2027 {
2028 	/*
2029 	 * Note that rcp depends on the same file descriptor being both
2030 	 * input and output to the remote side.  This is bogus, especially
2031 	 * when rcp is being run by a rsh that pipes. Fix it here because
2032 	 * it would require significantly more work in other places.
2033 	 * --hartmans 1/96
2034 	 */
2035 
2036 	if (fd == 0)
2037 		fd = 1;
2038 	return ((int)deswrite(fd, buf, len, 0));
2039 }
2040 
2041 static char **
2042 save_argv(int argc, char **argv)
2043 {
2044 	int i;
2045 
2046 	char **local_argv = (char **)calloc((unsigned)argc + 1,
2047 	    (unsigned)sizeof (char *));
2048 
2049 	/*
2050 	 * allocate an extra pointer, so that it is initialized to NULL and
2051 	 * execv() will work
2052 	 */
2053 	for (i = 0; i < argc; i++) {
2054 		local_argv[i] = strsave(argv[i]);
2055 	}
2056 
2057 	return (local_argv);
2058 }
2059 
2060 #define	SIZEOF_INADDR sizeof (struct in_addr)
2061 
2062 static void
2063 answer_auth(char *config_file, char *ccache_file)
2064 {
2065 	krb5_data pname_data, msg;
2066 	krb5_creds creds, *new_creds;
2067 	krb5_ccache cc;
2068 	krb5_auth_context auth_context = NULL;
2069 
2070 	if (config_file) {
2071 		const char *filenames[2];
2072 
2073 		filenames[1] = NULL;
2074 		filenames[0] = config_file;
2075 		if (krb5_set_config_files(bsd_context, filenames))
2076 			exit(1);
2077 	}
2078 	(void) memset((char *)&creds, 0, sizeof (creds));
2079 
2080 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data))
2081 		exit(1);
2082 
2083 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem,
2084 	    &creds.second_ticket))
2085 		exit(1);
2086 
2087 	if (ccache_file == NULL) {
2088 		if (krb5_cc_default(bsd_context, &cc))
2089 			exit(1);
2090 	} else {
2091 		if (krb5_cc_resolve(bsd_context, ccache_file, &cc))
2092 			exit(1);
2093 	}
2094 
2095 	if (krb5_cc_get_principal(bsd_context, cc, &creds.client))
2096 		exit(1);
2097 
2098 	if (krb5_parse_name(bsd_context, pname_data.data, &creds.server))
2099 		exit(1);
2100 
2101 	krb5_xfree(pname_data.data);
2102 	if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds,
2103 	    &new_creds))
2104 		exit(1);
2105 
2106 	if (krb5_mk_req_extended(bsd_context, &auth_context,
2107 	    AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))
2108 		exit(1);
2109 
2110 	if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) {
2111 		krb5_xfree(msg.data);
2112 		exit(1);
2113 	}
2114 	/* setup eblock for des_read and write */
2115 	krb5_copy_keyblock(bsd_context, &new_creds->keyblock, &session_key);
2116 
2117 	/* OK process key */
2118 	eblock.crypto_entry = session_key->enctype;
2119 	eblock.key = (krb5_keyblock *)session_key;
2120 
2121 	init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL,
2122 	    &desinbuf, &desoutbuf, CLIENT, &eblock);
2123 	/* cleanup */
2124 	krb5_free_cred_contents(bsd_context, &creds);
2125 	krb5_free_creds(bsd_context, new_creds);
2126 	krb5_xfree(msg.data);
2127 }
2128 
2129 
2130 static void
2131 try_normal_rcp(int cur_argc, char **cur_argv)
2132 {
2133 	char *target;
2134 
2135 	/*
2136 	 * Reset all KRB5 relevant flags and set the
2137 	 * cmd-buffer so that normal rcp works
2138 	 */
2139 	krb5auth_flag = encrypt_flag = encrypt_done = 0;
2140 	cmd = cmd_orig;
2141 	cmd_sunw = cmd_sunw_orig;
2142 
2143 	if (cur_argc < 2)
2144 		usage();
2145 
2146 	if (cur_argc > 2)
2147 		targetshouldbedirectory = 1;
2148 
2149 	rem = -1;
2150 
2151 	prev_argc = cur_argc;
2152 	prev_argv = save_argv(cur_argc, cur_argv);
2153 
2154 	(void) init_service(krb5auth_flag);
2155 
2156 	if (target = colon(cur_argv[cur_argc - 1])) {
2157 		toremote(target, cur_argc, cur_argv);
2158 	} else {
2159 		tolocal(cur_argc, cur_argv);
2160 		if (targetshouldbedirectory)
2161 			verifydir(cur_argv[cur_argc - 1]);
2162 	}
2163 	exit(errs);
2164 	/* NOTREACHED */
2165 }
2166 
2167 
2168 static int
2169 init_service(int krb5flag)
2170 {
2171 	struct servent *sp;
2172 	boolean_t success = B_FALSE;
2173 
2174 	if (krb5flag > 0) {
2175 		sp = getservbyname("kshell", "tcp");
2176 		if (sp == NULL) {
2177 			(void) fprintf(stderr,
2178 				gettext("rcp: kshell/tcp: unknown service.\n"
2179 				"trying normal shell/tcp service\n"));
2180 		} else {
2181 			portnumber = sp->s_port;
2182 			success = B_TRUE;
2183 		}
2184 	} else {
2185 		portnumber = htons(IPPORT_CMDSERVER);
2186 		success = B_TRUE;
2187 	}
2188 	return (success);
2189 }
2190 
2191 /*PRINTFLIKE1*/
2192 static void
2193 error(char *fmt, ...)
2194 {
2195 	va_list ap;
2196 	char buf[RCP_BUFSIZE];
2197 	char *cp = buf;
2198 
2199 	va_start(ap, fmt);
2200 	errs++;
2201 	*cp++ = 1;
2202 	(void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap);
2203 	va_end(ap);
2204 
2205 	(void) desrcpwrite(rem, buf, strlen(buf));
2206 	if (iamremote == 0)
2207 		(void) write(2, buf + 1, strlen(buf + 1));
2208 }
2209 
2210 static void
2211 addargs(char **arglist, ...)
2212 {
2213 	va_list ap;
2214 	int i = 0;
2215 	char *pm;
2216 
2217 	va_start(ap, arglist);
2218 	while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL)
2219 		if (strcmp(pm, ""))
2220 			arglist[i++] = pm;
2221 	arglist[i] = NULL;
2222 	va_end(ap);
2223 }
2224