home *** CD-ROM | disk | FTP | other *** search
- /*
- * rsh.c --- Remote execute command
- *
- * Author: ppessi <Pekka.Pessi@hut.fi>
- *
- * Created : Sat Oct 30 17:07:21 1993 ppessi
- * Last modified: Sun Feb 27 01:02:59 1994 ppessi
- *
- * $Log: rsh.c,v $
- * Revision 5.7 1994/05/15 21:53:22 jraja
- * Changed "rem" socket to not be put on FIONBIO state (which caused the
- * talker process to exhaust AmiTCP/IP internal buffers).
- *
- * Revision 5.6 1994/05/02 19:57:16 jraja
- * Removed compiler dependent macro definitions, which are in sys/cdefs.h.
- *
- * Revision 5.5 1994/02/26 23:03:05 ppessi
- * Updated to amitcp/socketbasetags.h
- *
- * Revision 5.4 1994/02/24 04:18:03 ppessi
- * Added AmiTCP 3 net.lib compatibility
- *
- * Revision 5.3 1994/01/21 15:09:45 ppessi
- * Added autodoc
- *
- * Revision 5.2 1994/01/20 17:00:16 ppessi
- * Amigaized version
- *
- */
-
- /****** utilities/rsh ********************************************************
-
- NAME
- rsh - remote shell
-
- VERSION
- $Id: rsh.c,v 5.7 1994/05/15 21:53:22 jraja Exp $
-
- SYNOPSIS
- rsh [-n] [-l username] host [command]
-
- DESCRIPTION
- Rsh executes command on remote Unix host.
-
- Rsh copies its standard input to the remote command, the standard
- output of the remote command to its standard output, and the
- standard error of the remote command to its standard error. Break
- signals C, E and F are propagated to the remote command as
- interrupt, quit and terminate signals, respectively; rsh normally
- terminates when the remote command does. The options are as follows:
-
- -l By default, the remote username is the same as the local
- username. The -l option allows the remote name to be
- specified. Authorization is determined as in rlogin(1).
-
- -n The -n option redirects input from the special device NIL:
-
- If no command is specified, you will be logged in on the remote host
- using rlogin.
-
- Shell metacharacters which are not quoted are interpreted on local
- machine, while quoted metacharacters are interpreted on the remote
- machine. For example, the command
-
- rsh otherhost cat remotefile >> localfile
-
- appends the remote file remotefile to the local file localfile,
- while
-
- rsh otherhost cat remotefile ">>" other_remotefile
-
- appends remotefile to other_remotefile.
-
- SEE ALSO
- rlogin
-
- HISTORY
- The rsh command appeared in 4.2BSD.
-
- ****************************************************************************
- */
-
- /* Kerberos options:
-
- -K The -K option turns off all Kerberos authentication.
-
- -d The -d option turns on socket debugging (using setsockopt(2)) on
- the TCP sockets used for communication with the remote host.
-
- -k The -k option causes rsh to obtain tickets for the remote host in
- realm instead of the remote host's realm as determined by
- krb_realmofhost(3).
-
- -x The -x option turns on DES encryption for all data exchange. This
- may introduce a significant delay in response time.
- */
-
- #include "rsh_rev.h"
- const char version[] = VERSTAG " "
- "Copyright © 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>\n"
- "Helsinki University of Technology, Finland.\n"
- "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
- All rights reserved.\n";
-
- /*-
- * Copyright (c) 1983, 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
- #ifdef AMIGA
- /*
- * Amiga specific initialization
- */
- #include <sys/types.h>
- #if __SASC
- #include <proto/dos.h>
- #include <clib/exec_protos.h>
- #include <pragmas/exec_sysbase_pragmas.h>
- extern struct ExecBase *SysBase;
- #include <proto/socket.h>
- #include <proto/usergroup.h>
- #elif __GNUC__
- #include <inline/dos.h>
- #include <inline/exec.h>
- #include <inline/socket.h>
- #error There is no <inline/usergroup.h>
- #else
- #include <clib/dos_protos.h>
- #include <clib/exec_protos.h>
- #include <clib/socket_protos.h>
- #include <clib/usergroup_protos.h>
- #endif
- #include <clib/netlib_protos.h>
-
- #define ioctl IoctlSocket
- #define close CloseSocket
-
- #define BUFSIZ 4096
-
- /*
- * UNIX processes, signals
- */
- #define pid_t APTR
- #define getpid() ((long)FindTask(0L))
-
- #define SIGINT 2 /* interrupt, generated from terminal special char */
- #define SIGQUIT 3 /* (*) quit, generated from terminal special char */
- #define SIGTERM 15 /* software termination signal */
-
- #else
- #include <sys/types.h>
- #include <sys/signal.h>
- #define Stdout 1
- #define Stderr 2
- #endif /* AMIGA */
-
- #include <sys/socket.h>
- #include <sys/ioctl.h>
-
- #include <netinet/in.h>
- #include <netdb.h>
-
- #include <pwd.h>
- #include <errno.h>
- #ifdef AMIGA
- #include "dosio.h"
- #else
- #include <stdio.h>
- #endif
- #include <string.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include "pathnames.h"
-
- #ifdef KERBEROS
- #include <kerberosIV/des.h>
- #include <kerberosIV/krb.h>
-
- CREDENTIALS cred;
- Key_schedule schedule;
- int use_kerberos = 1, doencrypt;
- char dst_realm_buf[REALM_SZ], *dest_realm;
- extern char *krb_realmofhost();
- #endif
-
- /*
- * rsh - remote shell
- */
- struct MsgPort *dadp = NULL;
-
- struct SocketMessage {
- struct Message sm_Msg;
- long sm_id;
- long sm_retval; /* non zero errorcode */
- long sm_errno; /* net error */
- } *exitm = NULL;
-
- int listener(register int rem, int rfd2);
- void sendsig(int s, char signo);
- char *copyargs(char **argv);
- void usage(void);
-
- pid_t start_talker(int rem);
- void stop_talker(pid_t sender_pid);
- int wait_talker(void);
-
- int
- main(int argc, char **argv)
- {
- extern char *optarg;
- extern int optind;
- struct passwd *pw;
- struct servent *sp;
- int argoff, asrsh, ch, dflag, nflag, one, rem, uid, retval;
- int rfd2;
- pid_t pid;
- register char *p;
- char *args, *host, *luser, *user;
-
- retval = argoff = asrsh = dflag = nflag = 0;
- one = 1;
- host = user = NULL;
-
- /* if called as something other than "rsh", use it as the host name */
- if (p = rindex(argv[0], '/'))
- ++p;
- else
- p = argv[0];
- if (strcmp(p, "rsh"))
- host = p;
- else
- asrsh = 1;
-
- /* handle "rsh host flags" */
- if (!host && argc > 2 && argv[1][0] != '-') {
- host = argv[1];
- argoff = 1;
- }
-
- #ifdef KERBEROS
- #ifdef CRYPT
- #define OPTIONS "8KLdek:l:nwx"
- #else
- #define OPTIONS "8KLdek:l:nw"
- #endif
- #else
- #define OPTIONS "8KLdel:nw"
- #endif
- while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
- switch(ch) {
- case 'K':
- #ifdef KERBEROS
- use_kerberos = 0;
- #endif
- break;
- case 'L': /* -8Lew are ignored to allow rlogin aliases */
- case 'e':
- case 'w':
- case '8':
- break;
- case 'd':
- dflag = 1;
- break;
- case 'l':
- user = optarg;
- break;
- #ifdef KERBEROS
- case 'k':
- dest_realm = dst_realm_buf;
- strncpy(dest_realm, optarg, REALM_SZ);
- break;
- #endif
- case 'n':
- nflag = 1;
- break;
- #if defined(KERBEROS) && defined(CRYPT)
- case 'x':
- doencrypt = 1;
- des_set_key(cred.session, schedule);
- break;
- #endif
- case '?':
- default:
- usage();
- }
- optind += argoff;
-
- /* if haven't gotten a host yet, do so */
- if (!host && !(host = argv[optind++]))
- usage();
-
- /* if no further arguments, must have been called as rlogin. */
- if (!argv[optind]) {
- #ifndef AMIGA
- if (asrsh)
- *argv = "rlogin";
- execv(_PATH_RLOGIN, argv);
- #endif
- (void)fprintf(stderr, "rsh: can't exec %s.\n", _PATH_RLOGIN);
- exit(1);
- }
-
- argc -= optind;
- argv += optind;
-
- #ifdef HAVE_GETUID
- if (!(pw = getpwuid(uid = getuid()))) {
- (void)fprintf(stderr, "rsh: unknown user id.\n");
- exit(1);
- }
- luser = pw->pw_name;
- #else
- if (!(luser = getenv("USER"))) {
- (void)fprintf(stderr, "rsh: unknown user.\n");
- exit(1);
- }
- #endif
- if (!user)
- user = luser;
-
- #if defined(KERBEROS) && defined(CRYPT)
- /* -x turns off -n */
- If (doencrypt)
- nflag = 0;
- #endif
-
- args = copyargs(argv);
-
- sp = NULL;
- #ifdef KERBEROS
- if (use_kerberos) {
- sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
- if (sp == NULL) {
- use_kerberos = 0;
- warning("can't get entry for %s/tcp service",
- doencrypt ? "ekshell" : "kshell");
- }
- }
- #endif
- if (sp == NULL)
- sp = getservbyname("shell", "tcp");
- if (sp == NULL) {
- (void)fprintf(stderr, "rsh: shell/tcp: unknown service.\n");
- exit(1);
- }
-
- #ifdef KERBEROS
- try_connect:
- if (use_kerberos) {
- rem = KSUCCESS;
- errno = 0;
- if (dest_realm == NULL)
- dest_realm = krb_realmofhost(host);
-
- #ifdef CRYPT
- if (doencrypt)
- rem = krcmd_mutual(&host, sp->s_port, user, args,
- &rfd2, dest_realm, &cred, schedule);
- else
- #endif
- rem = krcmd(&host, sp->s_port, user, args, &rfd2,
- dest_realm);
- if (rem < 0) {
- use_kerberos = 0;
- sp = getservbyname("shell", "tcp");
- if (sp == NULL) {
- (void)fprintf(stderr,
- "rsh: unknown service shell/tcp.\n");
- exit(1);
- }
- if (errno == ECONNREFUSED)
- warning("remote host doesn't support Kerberos");
- if (errno == ENOENT)
- warning("can't provide Kerberos auth data");
- goto try_connect;
- }
- } else {
- if (doencrypt) {
- (void)fprintf(stderr,
- "rsh: the -x flag requires Kerberos authentication.\n");
- exit(1);
- }
- rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
- }
- if (rem < 0)
- exit(1);
- #else
- rem = rcmd(&host, sp->s_port, luser, user, args, &rfd2);
- if (rem < 0) {
- perror("rcmd");
- exit(1);
- }
- #endif
- if (rfd2 < 0) {
- (void)fprintf(stderr, "rsh: can't establish stderr.\n");
- exit(1);
- }
- if (dflag &&
- (setsockopt(rem, SOL_SOCKET, SO_DEBUG,
- (caddr_t)&one, sizeof(one)) < 0 ||
- setsockopt(rfd2, SOL_SOCKET, SO_DEBUG,
- (caddr_t)&one, sizeof(one)) < 0)) {
- perror("rsh: setsockopt");
- }
-
- #ifdef AMIGA
- /* We are handling signals by ourselves */
- SetSocketSignals(SIGBREAKF_CTRL_D, 0L, 0L);
- #endif
- #ifdef HAVE_SETUID
- (void)setuid(uid);
- #endif
- if (!nflag) {
- pid = start_talker(rem);
- if (!pid) {
- (void)PrintFault(IoErr(), "rsh: CreateProcess");
- exit(1);
- }
- }
-
- #if defined(KERBEROS) && defined(CRYPT)
- if (!doencrypt)
- #endif
- {
- (void)ioctl(rfd2, FIONBIO, (caddr_t)&one);
- #ifndef AMIGA /* this would affect the talker process, too */
- (void)ioctl(rem, FIONBIO, (caddr_t)&one);
- #endif
- }
-
- retval = listener(rem, rfd2);
-
- if (!nflag) {
- int retval2;
- stop_talker(pid);
- retval2 = wait_talker();
- if (retval2 != 0 && retval2 > retval)
- exit(retval2);
- }
- exit(retval);
- }
-
- /*
- * listener --- print stdout and stderr
- * --- send signals
- */
- int listener(register int rem, int rfd2)
- {
- register int cc;
- int readfrom, ready;
- ULONG breaks, received;
- char *buf = malloc(BUFSIZ);
-
- if (!buf) {
- perror("rsh: malloc");
- return 1;
- }
-
- readfrom = (1 << rfd2) | (1 << rem);
- breaks = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F;
- do {
- ready = readfrom;
- received = breaks;
- if (WaitSelect(16, (fd_set *)&ready, 0, 0, 0, &received) < 0) {
- /* CTRL_D ? */
- if (errno == EINTR) {
- return 128;
- } else {
- (void)perror("rsh: select");
- return 1;
- }
- continue;
- }
- if (received & SIGBREAKF_CTRL_C) {
- sendsig(rfd2, SIGINT);
- }
- if (received & SIGBREAKF_CTRL_E) {
- sendsig(rfd2, SIGQUIT);
- }
- if (received & SIGBREAKF_CTRL_F) {
- sendsig(rfd2, SIGTERM);
- }
- if (ready & (1 << rfd2)) {
- errno = 0;
- #if defined(KERBEROS) && defined(CRYPT)
- if (doencrypt)
- cc = des_recv(rfd2, buf, sizeof buf, 0);
- else
- #endif
- cc = recv(rfd2, buf, sizeof buf, 0);
- if (cc <= 0) {
- if (errno != EWOULDBLOCK)
- readfrom &= ~(1 << rfd2);
- } else
- (void)write(Stderr, buf, cc);
- }
- if (ready & (1 << rem)) {
- errno = 0;
- #if defined(KERBEROS) && defined(CRYPT)
- if (doencrypt)
- cc = des_recv(rem, buf, sizeof buf, 0);
- else
- #endif
- cc = recv(rem, buf, sizeof buf, 0);
- if (cc <= 0) {
- if (errno != EWOULDBLOCK)
- readfrom &= ~(1 << rem);
- } else
- (void)write(Stdout, buf, cc);
- }
- } while (readfrom);
-
- return 0;
- }
-
- void sendsig(int rfd2, char signo)
- {
- #if defined(KERBEROS) && defined(CRYPT)
- if (doencrypt)
- (void)des_send(rfd2, &signo, 1);
- else
- #endif
- (void)send(rfd2, &signo, 1, 0);
- }
-
- #ifdef KERBEROS
- /* VARARGS */
- void warning(char * fmt, ...)
- {
- #error notyet ready
- va_list ap;
- char *fmt;
-
- (void)fprintf(stderr, "rsh: warning, using standard rsh: ");
- va_start(ap);
- fmt = va_arg(ap, char *);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void)fprintf(stderr, ".\n");
- }
- #endif
-
- char *
- copyargs(char **argv)
- {
- register int cc;
- register char **ap, *p;
- char *args;
-
- cc = 0;
- for (ap = argv; *ap; ++ap)
- cc += strlen(*ap) + 1;
- if (!(args = malloc((u_int)cc))) {
- (void)fprintf(stderr, "rsh: %s.\n", strerror(ENOMEM));
- exit(1);
- }
- for (p = args, ap = argv; *ap; ++ap) {
- (void)strcpy(p, *ap);
- for (p = strcpy(p, *ap); *p; ++p);
- if (ap[1])
- *p++ = ' ';
- }
- return(args);
- }
-
- void
- usage(void)
- {
- (void)fprintf(stderr,
- "usage: rsh [-nd%s]%s[-l login] host [command]\n",
- #ifdef KERBEROS
- #ifdef CRYPT
- "x", " [-k realm] ");
- #else
- "", " [-k realm] ");
- #endif
- #else
- "", " ");
- #endif
- exit(1);
- }
-
- /*
- * Amiga Process handling
- */
-
- #include <exec/memory.h>
- #include <dos/dostags.h>
-
- void free_talker(void);
- ASM LONG exitcode(REG(d0) LONG status, REG(d1) LONG exitmessage);
- SAVEDS LONG do_talk(void);
- int talker(struct Library *SocketBase, struct Library *DOSBase, int rem,
- int *pErrno);
-
- pid_t start_talker(int rem)
- {
- struct Process *pid = NULL;
-
- dadp = CreateMsgPort();
- exitm = CreateIORequest(dadp, sizeof(*exitm));
-
- atexit(free_talker);
-
- if (!exitm || !dadp) {
- fprintf(stderr, "rsh: memory or signals exhausted\n");
- return NULL;
- }
-
- if ((exitm->sm_id = ReleaseCopyOfSocket(rem, -1L)) == -1) {
- perror("ReleaseCopyOfSocket");
- return NULL;
- }
-
- pid = CreateNewProcTags(NP_Entry, do_talk,
- NP_Name, "rsh talker",
- NP_Output, Stderr,
- NP_CloseOutput, FALSE,
- NP_Input, Stdin,
- NP_CloseInput, FALSE,
- NP_ExitCode, exitcode,
- NP_ExitData, exitm,
- TAG_END, NULL);
- if (pid == 0) {
- /* We should not leave orphan sockets into the internal list */
- (void)ObtainSocket((LONG)exitm->sm_id, PF_INET, SOCK_STREAM, NULL);
- }
-
- return (pid_t)pid;
- }
-
- void stop_talker(pid_t pid)
- {
- struct Process *talker_process = (struct Process *)pid;
- Forbid();
- /* Check that sender task is still around */
- /* i.e. the exitm Message is not replied */
- if (talker_process && dadp->mp_MsgList.lh_Head != exitm)
- Signal((struct Task *)talker_process, SIGBREAKF_CTRL_C);
- Permit();
- }
-
- int wait_talker(void)
- {
- do {
- WaitPort(dadp);
- } while (GetMsg(dadp) != exitm);
- return exitm->sm_retval;
- }
-
- void free_talker(void)
- {
- if (exitm) DeleteIORequest(exitm);
- exitm = NULL;
-
- if (dadp) DeleteMsgPort(dadp);
- dadp = NULL;
- }
-
- /*
- * Code executed in the talker's context
- */
- #ifdef AMITCP3
- #include <amitcp/socketbasetags.h>
- /*
- * A local version of PrintNetFault()
- * (as opposed to version in link library)
- */
- #define PrintNetFault(error, banner) do { \
- ULONG __pnf_taglist[3]; \
- __pnf_taglist[0] = SBTM_GETVAL(SBTC_ERRNOSTRPTR); \
- __pnf_taglist[1] = (error); \
- __pnf_taglist[2] = TAG_END; \
- SocketBaseTagList((struct TagItem *)__pnf_taglist); \
- Printf("%s: %s\n", (banner), __pnf_taglist[1]); \
- } while(0)
- #endif
-
- #define SysBase (*(struct ExecBase **)4)
-
- ASM LONG exitcode(REG(d0) LONG status, REG(d1) LONG exitmessage)
- {
- ((struct SocketMessage *)exitmessage)->sm_retval = status;
- ReplyMsg((struct Message *)exitmessage);
- return 0;
- }
-
- SAVEDS LONG do_talk(void)
- {
- int s, retval = 20, errno;
- struct SocketMessage *exitm = (struct SocketMessage*)
- ((struct Process*)FindTask(NULL))->pr_ExitData;
- struct Library *DOSBase = OpenLibrary("dos.library", 37L);
- struct Library *SocketBase = OpenLibrary("bsdsocket.library", 3L);
-
- if (DOSBase && SocketBase) {
-
- #ifdef AMITCP3
- SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno,
- SBTC_BREAKMASK, SIGBREAKF_CTRL_C,
- TAG_END, NULL);
- #else
- SetErrnoPtr(&errno, sizeof(errno));
- SetSocketSignals(SIGBREAKF_CTRL_C, 0L, 0L);
- #endif
- s = ObtainSocket(exitm->sm_id, PF_INET, SOCK_STREAM, NULL);
- if (s != -1) {
- retval = talker(SocketBase, DOSBase, s, &errno);
- } else {
- PrintNetFault(errno, "ObtainSocket");
- }
- }
- if (DOSBase)
- CloseLibrary(DOSBase);
- if (SocketBase)
- CloseLibrary(SocketBase);
-
- Flush(Output());
- return retval;
- }
-
- /*
- * Read from Input(), write to remote process
- */
- int talker(struct Library *SocketBase, struct Library *DOSBase, int rem,
- int *pErrno)
- {
- int l, m = 0, n = 0, istty;
- int retval = 0;
- char *cb = NULL, *tb;
-
- BPTR InFh = Input();
-
- if (InFh) {
- istty = IsInteractive(InFh);
- #define TTYBUFSIZ 8
- cb = AllocVec(istty ? TTYBUFSIZ : BUFSIZ, MEMF_PUBLIC);
- if (!cb) {
- PrintNetFault(ENOMEM, "rsh: AllocVec");
- retval = 2;
- goto Return;
- }
-
- for (;;) {
- if (SetSignal(0L, 0L) & SIGBREAKF_CTRL_C)
- goto Return;
-
- if (istty) {
- /* Wait a char for a second */
- if (!WaitForChar(InFh, 1000000))
- continue; /* timeout */
- /* Get characters from Input() */
- n = 0;
- while((m = Read(InFh, cb + n, 1)) > 0
- && ++n < TTYBUFSIZ
- && WaitForChar(InFh, 0))
- ;
- if (n > 0) /* error may be hidden if have any chars */
- m = n;
- }
- else {
- m = Read(InFh, cb, BUFSIZ);
- }
-
- if (m <= 0) {
- if (m == -1) {
- PrintFault(IoErr(), "Read");
- retval = 2;
- }
- /* else EOF */
- goto Return;
- }
-
- tb = cb;
- while ( m > 0 ) {
- if ( (l = send(rem, tb, m, 0)) < 0 ) {
- if (*pErrno != EINTR) {
- PrintNetFault(*pErrno,
- "rsh: send");
- }
- retval = 2;
- goto Return;
- }
- tb += l;
- m -= l;
- }
- }
- }
-
- Return:
- shutdown(rem, 1); /* no more sends */
- if (cb) FreeVec(cb);
-
- return retval;
- }
-