1/*
2 * Copyright (c) 1995 Danny Gasparovski.
3 *
4 * Please read the file COPYRIGHT for the
5 * terms and conditions of the copyright.
6 */
7
8#define WANT_SYS_IOCTL_H
9#include <slirp.h>
10
11u_int curtime, time_fasttimo, last_slowtimo, detach_time;
12u_int detach_wait = 600000;	/* 10 minutes */
13struct emu_t *tcpemu;
14
15int
16inet_strtoip(const char*  str, uint32_t  *ip)
17{
18    int  comp[4];
19
20    if (sscanf(str, "%d.%d.%d.%d", &comp[0], &comp[1], &comp[2], &comp[3]) != 4)
21        return -1;
22
23    if ((unsigned)comp[0] >= 256 ||
24        (unsigned)comp[1] >= 256 ||
25        (unsigned)comp[2] >= 256 ||
26        (unsigned)comp[3] >= 256)
27        return -1;
28
29    *ip = (uint32_t)((comp[0] << 24) | (comp[1] << 16) |
30                     (comp[2] << 8)  |  comp[3]);
31    return 0;
32}
33
34char*  inet_iptostr(uint32_t  ip)
35{
36    static char  buff[32];
37
38    snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
39             (ip >> 24) & 255,
40             (ip >> 16) & 255,
41             (ip >> 8) & 255,
42             ip & 255);
43    return buff;
44}
45
46/*
47 * Get our IP address and put it in our_addr
48 */
49void
50getouraddr()
51{
52    char*        hostname = host_name();
53    SockAddress  hostaddr;
54
55    our_addr_ip = loopback_addr_ip;
56
57    if (sock_address_init_resolve( &hostaddr, hostname, 0, 0 ) < 0)
58        return;
59
60    our_addr_ip = sock_address_get_ip(&hostaddr);
61    if (our_addr_ip == (uint32_t)-1)
62        our_addr_ip = loopback_addr_ip;
63}
64
65struct quehead {
66	struct quehead *qh_link;
67	struct quehead *qh_rlink;
68};
69
70inline void
71insque(void *a, void *b)
72{
73	register struct quehead *element = (struct quehead *) a;
74	register struct quehead *head = (struct quehead *) b;
75	element->qh_link = head->qh_link;
76	head->qh_link = (struct quehead *)element;
77	element->qh_rlink = (struct quehead *)head;
78	((struct quehead *)(element->qh_link))->qh_rlink
79	= (struct quehead *)element;
80}
81
82inline void
83remque(void *a)
84{
85  register struct quehead *element = (struct quehead *) a;
86  ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
87  ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
88  element->qh_rlink = NULL;
89  /*  element->qh_link = NULL;  TCP FIN1 crashes if you do this.  Why ? */
90}
91
92/* #endif */
93
94
95int
96add_exec(struct ex_list **ex_ptr, int do_pty, char *exec, int addr, int port)
97{
98	struct ex_list *tmp_ptr;
99
100	/* First, check if the port is "bound" */
101	for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
102		if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr)
103		   return -1;
104	}
105
106	tmp_ptr = *ex_ptr;
107	*ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
108	(*ex_ptr)->ex_fport = port;
109	(*ex_ptr)->ex_addr = addr;
110	(*ex_ptr)->ex_pty = do_pty;
111	(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
112	(*ex_ptr)->ex_next = tmp_ptr;
113	return 0;
114}
115
116#ifndef HAVE_STRERROR
117
118/*
119 * For systems with no strerror
120 */
121
122extern int sys_nerr;
123extern char *sys_errlist[];
124
125char *
126strerror(error)
127	int error;
128{
129	if (error < sys_nerr)
130	   return sys_errlist[error];
131	else
132	   return "Unknown error.";
133}
134
135#endif
136
137
138#ifdef _WIN32
139
140int
141fork_exec(struct socket *so, const char *ex, int do_pty)
142{
143    /* not implemented */
144    return 0;
145}
146
147#else
148
149#ifndef CONFIG_QEMU
150int
151slirp_openpty(amaster, aslave)
152     int *amaster, *aslave;
153{
154	register int master, slave;
155
156#ifdef HAVE_GRANTPT
157	char *ptr;
158
159	if ((master = open("/dev/ptmx", O_RDWR)) < 0 ||
160	    grantpt(master) < 0 ||
161	    unlockpt(master) < 0 ||
162	    (ptr = ptsname(master)) == NULL)  {
163		close(master);
164		return -1;
165	}
166
167	if ((slave = open(ptr, O_RDWR)) < 0 ||
168	    ioctl(slave, I_PUSH, "ptem") < 0 ||
169	    ioctl(slave, I_PUSH, "ldterm") < 0 ||
170	    ioctl(slave, I_PUSH, "ttcompat") < 0) {
171		close(master);
172		close(slave);
173		return -1;
174	}
175
176	*amaster = master;
177	*aslave = slave;
178	return 0;
179
180#else
181
182	static char line[] = "/dev/ptyXX";
183	register const char *cp1, *cp2;
184
185	for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
186		line[8] = *cp1;
187		for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
188			line[9] = *cp2;
189			if ((master = open(line, O_RDWR, 0)) == -1) {
190				if (errno == ENOENT)
191				   return (-1);    /* out of ptys */
192			} else {
193				line[5] = 't';
194				/* These will fail */
195				(void) chown(line, getuid(), 0);
196				(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
197#ifdef HAVE_REVOKE
198				(void) revoke(line);
199#endif
200				if ((slave = open(line, O_RDWR, 0)) != -1) {
201					*amaster = master;
202					*aslave = slave;
203					return 0;
204				}
205				(void) close(master);
206				line[5] = 'p';
207			}
208		}
209	}
210	errno = ENOENT; /* out of ptys */
211	return (-1);
212#endif
213}
214#endif
215
216/*
217 * XXX This is ugly
218 * We create and bind a socket, then fork off to another
219 * process, which connects to this socket, after which we
220 * exec the wanted program.  If something (strange) happens,
221 * the accept() call could block us forever.
222 *
223 * do_pty = 0   Fork/exec inetd style
224 * do_pty = 1   Fork/exec using slirp.telnetd
225 * do_ptr = 2   Fork/exec using pty
226 */
227int
228fork_exec(struct socket *so, const char *ex, int do_pty)
229{
230	int s;
231        int master = -1;
232	const char *argv[256];
233#if 0
234	char buff[256];
235#endif
236	/* don't want to clobber the original */
237	char *bptr;
238	const char *curarg;
239	int c, i;
240
241	DEBUG_CALL("fork_exec");
242	DEBUG_ARG("so = %lx", (long)so);
243	DEBUG_ARG("ex = %lx", (long)ex);
244	DEBUG_ARG("do_pty = %lx", (long)do_pty);
245
246	if (do_pty == 2) {
247#if 0
248		if (slirp_openpty(&master, &s) == -1) {
249			lprint("Error: openpty failed: %s\n", strerror(errno));
250			return 0;
251		}
252#else
253                return 0;
254#endif
255	} else {
256		if ((s = socket_anyaddr_server(0, SOCKET_STREAM)) < 0) {
257			lprint("Error: inet socket: %s\n", errno_str);
258			return 0;
259		}
260	}
261
262	switch(fork()) {
263	 case -1:
264		lprint("Error: fork failed: %s\n", strerror(errno));
265		close(s);
266		if (do_pty == 2)
267		   close(master);
268		return 0;
269
270	 case 0:
271		/* Set the DISPLAY */
272		if (do_pty == 2) {
273			(void) close(master);
274#ifdef TIOCSCTTY /* XXXXX */
275			(void) setsid();
276			ioctl(s, TIOCSCTTY, (char *)NULL);
277#endif
278		} else {
279			SockAddress  addr;
280			socket_get_address(s, &addr);
281			socket_close(s);
282			/*
283			 * Connect to the socket
284			 * XXX If any of these fail, we're in trouble!
285	 		 */
286            s = socket_loopback_client(sock_address_get_port(&addr), SOCKET_STREAM);
287		}
288
289#if 0
290		if (x_port >= 0) {
291#ifdef HAVE_SETENV
292			sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
293			setenv("DISPLAY", buff, 1);
294#else
295			sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
296			putenv(buff);
297#endif
298		}
299#endif
300		dup2(s, 0);
301		dup2(s, 1);
302		dup2(s, 2);
303		for (s = getdtablesize() - 1; s >= 3; s--)
304		   close(s);
305
306		i = 0;
307		bptr = strdup(ex); /* No need to free() this */
308		if (do_pty == 1) {
309			/* Setup "slirp.telnetd -x" */
310			argv[i++] = "slirp.telnetd";
311			argv[i++] = "-x";
312			argv[i++] = bptr;
313		} else
314		   do {
315			/* Change the string into argv[] */
316			curarg = bptr;
317			while (*bptr != ' ' && *bptr != (char)0)
318			   bptr++;
319			c = *bptr;
320			*bptr++ = (char)0;
321			argv[i++] = strdup(curarg);
322		   } while (c);
323
324                argv[i] = NULL;
325		execvp(argv[0], (char **)argv);
326
327		/* Ooops, failed, let's tell the user why */
328		  {
329			  char buff[256];
330                          int ret;
331
332			  snprintf(buff, sizeof(buff),
333                                   "Error: execvp of %s failed: %s\n",
334                                   argv[0], strerror(errno));
335			  do {
336                                ret =write(2, buff, strlen(buff)+1);
337                          } while (ret < 0 && errno == EINTR);
338		  }
339		close(0); close(1); close(2); /* XXX */
340		exit(1);
341
342	 default:
343		if (do_pty == 2) {
344			close(s);
345			so->s = master;
346		} else {
347			/*
348			 * XXX this could block us...
349			 * XXX Should set a timer here, and if accept() doesn't
350		 	 * return after X seconds, declare it a failure
351		 	 * The only reason this will block forever is if socket()
352		 	 * of connect() fail in the child process
353		 	 */
354             so->s = socket_accept(s, NULL);
355			 socket_set_xreuseaddr(so->s);
356             socket_set_oobinline(so->s);
357		}
358		socket_set_nonblock(so->s);
359
360		/* Append the telnet options now */
361                if (so->so_m != NULL && do_pty == 1)  {
362			sbappend(so, so->so_m);
363                        so->so_m = NULL;
364		}
365
366		return 1;
367	}
368}
369#endif
370
371#ifndef HAVE_STRDUP
372char *
373strdup(const char*  str)
374{
375	char *bptr;
376	int   len = strlen(str);
377
378	bptr = (char *)malloc(len+1);
379	memcpy(bptr, str, len+1);
380
381	return bptr;
382}
383#endif
384
385#if 0
386void
387snooze_hup(num)
388	int num;
389{
390	int s, ret;
391#ifndef NO_UNIX_SOCKETS
392	struct sockaddr_un sock_un;
393#endif
394	struct sockaddr_in sock_in;
395	char buff[256];
396
397	ret = -1;
398	if (slirp_socket_passwd) {
399		s = socket(AF_INET, SOCK_STREAM, 0);
400		if (s < 0)
401		   slirp_exit(1);
402		sock_in.sin_family = AF_INET;
403		sock_in.sin_addr.s_addr = slirp_socket_addr;
404		sock_in.sin_port = htons(slirp_socket_port);
405		if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0)
406		   slirp_exit(1); /* just exit...*/
407		sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit);
408		write(s, buff, strlen(buff)+1);
409	}
410#ifndef NO_UNIX_SOCKETS
411	  else {
412		s = socket(AF_UNIX, SOCK_STREAM, 0);
413		if (s < 0)
414		   slirp_exit(1);
415		sock_un.sun_family = AF_UNIX;
416		strcpy(sock_un.sun_path, socket_path);
417		if (connect(s, (struct sockaddr *)&sock_un,
418			      sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0)
419		   slirp_exit(1);
420		sprintf(buff, "kill none:%d", slirp_socket_unit);
421		write(s, buff, strlen(buff)+1);
422	}
423#endif
424	slirp_exit(0);
425}
426
427
428void
429snooze()
430{
431	sigset_t s;
432	int i;
433
434	/* Don't need our data anymore */
435	/* XXX This makes SunOS barf */
436/*	brk(0); */
437
438	/* Close all fd's */
439	for (i = 255; i >= 0; i--)
440	   close(i);
441
442	signal(SIGQUIT, slirp_exit);
443	signal(SIGHUP, snooze_hup);
444	sigemptyset(&s);
445
446	/* Wait for any signal */
447	sigsuspend(&s);
448
449	/* Just in case ... */
450	exit(255);
451}
452
453void
454relay(s)
455	int s;
456{
457	char buf[8192];
458	int n;
459	fd_set readfds;
460	struct ttys *ttyp;
461
462	/* Don't need our data anymore */
463	/* XXX This makes SunOS barf */
464/*	brk(0); */
465
466	signal(SIGQUIT, slirp_exit);
467	signal(SIGHUP, slirp_exit);
468        signal(SIGINT, slirp_exit);
469	signal(SIGTERM, slirp_exit);
470
471	/* Fudge to get term_raw and term_restore to work */
472	if (NULL == (ttyp = tty_attach (0, slirp_tty))) {
473         lprint ("Error: tty_attach failed in misc.c:relay()\r\n");
474         slirp_exit (1);
475    }
476	ttyp->fd = 0;
477	ttyp->flags |= TTY_CTTY;
478	term_raw(ttyp);
479
480	while (1) {
481		FD_ZERO(&readfds);
482
483		FD_SET(0, &readfds);
484		FD_SET(s, &readfds);
485
486		n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
487
488		if (n <= 0)
489		   slirp_exit(0);
490
491		if (FD_ISSET(0, &readfds)) {
492			n = read(0, buf, 8192);
493			if (n <= 0)
494			   slirp_exit(0);
495			n = writen(s, buf, n);
496			if (n <= 0)
497			   slirp_exit(0);
498		}
499
500		if (FD_ISSET(s, &readfds)) {
501			n = read(s, buf, 8192);
502			if (n <= 0)
503			   slirp_exit(0);
504			n = writen(0, buf, n);
505			if (n <= 0)
506			   slirp_exit(0);
507		}
508	}
509
510	/* Just in case.... */
511	exit(1);
512}
513#endif
514
515#ifdef CONFIG_QEMU
516#include "monitor/monitor.h"
517
518void lprint(const char *format, ...)
519{
520    va_list args;
521
522    va_start(args, format);
523    monitor_vprintf(cur_mon, format, args);
524    va_end(args);
525}
526#else
527int (*lprint_print) _P((void *, const char *, va_list));
528char *lprint_ptr, *lprint_ptr2, **lprint_arg;
529
530void
531#ifdef __STDC__
532lprint(const char *format, ...)
533#else
534lprint(va_alist) va_dcl
535#endif
536{
537	va_list args;
538
539#ifdef __STDC__
540        va_start(args, format);
541#else
542        char *format;
543        va_start(args);
544        format = va_arg(args, char *);
545#endif
546#if 0
547	/* If we're printing to an sbuf, make sure there's enough room */
548	/* XXX +100? */
549	if (lprint_sb) {
550		if ((lprint_ptr - lprint_sb->sb_wptr) >=
551		    (lprint_sb->sb_datalen - (strlen(format) + 100))) {
552			int deltaw = lprint_sb->sb_wptr - lprint_sb->sb_data;
553			int deltar = lprint_sb->sb_rptr - lprint_sb->sb_data;
554			int deltap = lprint_ptr -         lprint_sb->sb_data;
555
556			lprint_sb->sb_data = (char *)realloc(lprint_sb->sb_data,
557							     lprint_sb->sb_datalen + TCP_SNDSPACE);
558
559			/* Adjust all values */
560			lprint_sb->sb_wptr = lprint_sb->sb_data + deltaw;
561			lprint_sb->sb_rptr = lprint_sb->sb_data + deltar;
562			lprint_ptr =         lprint_sb->sb_data + deltap;
563
564			lprint_sb->sb_datalen += TCP_SNDSPACE;
565		}
566	}
567#endif
568	if (lprint_print)
569	   lprint_ptr += (*lprint_print)(*lprint_arg, format, args);
570
571	/* Check if they want output to be logged to file as well */
572	if (lfd) {
573		/*
574		 * Remove \r's
575		 * otherwise you'll get ^M all over the file
576		 */
577		int len = strlen(format);
578		char *bptr1, *bptr2;
579
580		bptr1 = bptr2 = strdup(format);
581
582		while (len--) {
583			if (*bptr1 == '\r')
584			   memcpy(bptr1, bptr1+1, len+1);
585			else
586			   bptr1++;
587		}
588		vfprintf(lfd, bptr2, args);
589		free(bptr2);
590	}
591	va_end(args);
592}
593
594void
595add_emu(buff)
596	char *buff;
597{
598	u_int lport, fport;
599	u_int8_t tos = 0, emu = 0;
600	char buff1[256], buff2[256], buff4[128];
601	char *buff3 = buff4;
602	struct emu_t *emup;
603	struct socket *so;
604
605	if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) {
606		lprint("Error: Bad arguments\r\n");
607		return;
608	}
609
610	if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) {
611		lport = 0;
612		if (sscanf(buff1, "%d", &fport) != 1) {
613			lprint("Error: Bad first argument\r\n");
614			return;
615		}
616	}
617
618	if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) {
619		buff3 = 0;
620		if (sscanf(buff2, "%256s", buff1) != 1) {
621			lprint("Error: Bad second argument\r\n");
622			return;
623		}
624	}
625
626	if (buff3) {
627		if (strcmp(buff3, "lowdelay") == 0)
628		   tos = IPTOS_LOWDELAY;
629		else if (strcmp(buff3, "throughput") == 0)
630		   tos = IPTOS_THROUGHPUT;
631		else {
632			lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n");
633			return;
634		}
635	}
636
637	if (strcmp(buff1, "ftp") == 0)
638	   emu = EMU_FTP;
639	else if (strcmp(buff1, "irc") == 0)
640	   emu = EMU_IRC;
641	else if (strcmp(buff1, "none") == 0)
642	   emu = EMU_NONE; /* ie: no emulation */
643	else {
644		lprint("Error: Unknown service\r\n");
645		return;
646	}
647
648	/* First, check that it isn't already emulated */
649	for (emup = tcpemu; emup; emup = emup->next) {
650		if (emup->lport == lport && emup->fport == fport) {
651			lprint("Error: port already emulated\r\n");
652			return;
653		}
654	}
655
656	/* link it */
657	emup = (struct emu_t *)malloc(sizeof (struct emu_t));
658	emup->lport = (u_int16_t)lport;
659	emup->fport = (u_int16_t)fport;
660	emup->tos = tos;
661	emup->emu = emu;
662	emup->next = tcpemu;
663	tcpemu = emup;
664
665	/* And finally, mark all current sessions, if any, as being emulated */
666	for (so = tcb.so_next; so != &tcb; so = so->so_next) {
667		if ((lport && lport == so->so_laddr_port) ||
668		    (fport && fport == so->so_faddr_port)) {
669			if (emu)
670			   so->so_emu = emu;
671			if (tos)
672			   so->so_iptos = tos;
673		}
674	}
675
676	lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport);
677}
678#endif
679
680#ifdef BAD_SPRINTF
681
682#undef vsprintf
683#undef sprintf
684
685/*
686 * Some BSD-derived systems have a sprintf which returns char *
687 */
688
689int
690vsprintf_len(string, format, args)
691	char *string;
692	const char *format;
693	va_list args;
694{
695	vsprintf(string, format, args);
696	return strlen(string);
697}
698
699int
700#ifdef __STDC__
701sprintf_len(char *string, const char *format, ...)
702#else
703sprintf_len(va_alist) va_dcl
704#endif
705{
706	va_list args;
707#ifdef __STDC__
708	va_start(args, format);
709#else
710	char *string;
711	char *format;
712	va_start(args);
713	string = va_arg(args, char *);
714	format = va_arg(args, char *);
715#endif
716	vsprintf(string, format, args);
717	return strlen(string);
718}
719
720#endif
721
722#if 0
723void
724u_sleep(int usec)
725{
726	struct timeval t;
727	fd_set fdset;
728
729	FD_ZERO(&fdset);
730
731	t.tv_sec = 0;
732	t.tv_usec = usec * 1000;
733
734	select(0, &fdset, &fdset, &fdset, &t);
735}
736#endif
737
738/*
739 * Set fd blocking and non-blocking
740 */
741
742void
743fd_nonblock(int fd)
744{
745#ifdef FIONBIO
746#ifdef _WIN32
747        unsigned long opt = 1;
748#else
749        int opt = 1;
750#endif
751
752	ioctlsocket(fd, FIONBIO, &opt);
753#else
754	int opt;
755
756	opt = fcntl(fd, F_GETFL, 0);
757	opt |= O_NONBLOCK;
758	fcntl(fd, F_SETFL, opt);
759#endif
760}
761
762void
763fd_block(int fd)
764{
765#ifdef FIONBIO
766#ifdef _WIN32
767        unsigned long opt = 0;
768#else
769	int opt = 0;
770#endif
771
772	ioctlsocket(fd, FIONBIO, &opt);
773#else
774	int opt;
775
776	opt = fcntl(fd, F_GETFL, 0);
777	opt &= ~O_NONBLOCK;
778	fcntl(fd, F_SETFL, opt);
779#endif
780}
781
782
783#if 0
784/*
785 * invoke RSH
786 */
787int
788rsh_exec(so,ns, user, host, args)
789	struct socket *so;
790	struct socket *ns;
791	char *user;
792	char *host;
793	char *args;
794{
795	int fd[2];
796	int fd0[2];
797	int s;
798	char buff[256];
799
800	DEBUG_CALL("rsh_exec");
801	DEBUG_ARG("so = %lx", (long)so);
802
803	if (pipe(fd)<0) {
804          lprint("Error: pipe failed: %s\n", strerror(errno));
805          return 0;
806	}
807/* #ifdef HAVE_SOCKETPAIR */
808#if 1
809        if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) {
810          close(fd[0]);
811          close(fd[1]);
812          lprint("Error: openpty failed: %s\n", strerror(errno));
813          return 0;
814        }
815#else
816        if (slirp_openpty(&fd0[0], &fd0[1]) == -1) {
817          close(fd[0]);
818          close(fd[1]);
819          lprint("Error: openpty failed: %s\n", strerror(errno));
820          return 0;
821        }
822#endif
823
824	switch(fork()) {
825	 case -1:
826           lprint("Error: fork failed: %s\n", strerror(errno));
827           close(fd[0]);
828           close(fd[1]);
829           close(fd0[0]);
830           close(fd0[1]);
831           return 0;
832
833	 case 0:
834           close(fd[0]);
835           close(fd0[0]);
836
837		/* Set the DISPLAY */
838           if (x_port >= 0) {
839#ifdef HAVE_SETENV
840             sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
841             setenv("DISPLAY", buff, 1);
842#else
843             sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
844             putenv(buff);
845#endif
846           }
847
848           dup2(fd0[1], 0);
849           dup2(fd0[1], 1);
850           dup2(fd[1], 2);
851           for (s = 3; s <= 255; s++)
852             close(s);
853
854           execlp("rsh","rsh","-l", user, host, args, NULL);
855
856           /* Ooops, failed, let's tell the user why */
857
858           sprintf(buff, "Error: execlp of %s failed: %s\n",
859                   "rsh", strerror(errno));
860           write(2, buff, strlen(buff)+1);
861           close(0); close(1); close(2); /* XXX */
862           exit(1);
863
864        default:
865          close(fd[1]);
866          close(fd0[1]);
867          ns->s=fd[0];
868          so->s=fd0[0];
869
870          return 1;
871	}
872}
873#endif
874