File netkit-rsh-0.17.diff of Package rsh

--- configure
+++ configure
@@ -62,7 +62,7 @@
 
 BINDIR="$EXECPREFIX/bin"
 SBINDIR="$EXECPREFIX/sbin"
-MANDIR="$PREFIX/man"
+MANDIR="$PREFIX/share/man"
 
 echo "Directories: $BINDIR $SBINDIR $MANDIR "
 
--- rcp/rcp.c
+++ rcp/rcp.c
@@ -262,9 +262,9 @@
 					nospace();
 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
 				host = thost;
-					rem = rcmd(&host, port, pwd->pw_name,
+					rem = rcmd_af(&host, port, pwd->pw_name,
 					    tuser ? tuser : pwd->pw_name,
-					    bp, 0);
+					    bp, 0, PF_UNSPEC);
 				if (rem < 0)
 					exit(1);
 #ifdef IP_TOS
@@ -325,7 +325,8 @@
 		if (!(bp = malloc(len)))
 			nospace();
 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
-			rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0);
+			rem = rcmd_af(&host, port, pwd->pw_name, suser, bp, 0,
+				      PF_UNSPEC);
 		(void)free(bp);
 		if (rem < 0) {
 			++errs;
--- rexec/rexec.1
+++ rexec/rexec.1
@@ -30,14 +30,17 @@
 .\" SUCH DAMAGE.
 .\"
 .\"
-.TH REXEC 1 "August 15, 1999"
+.TH REXEC 1 "February 14, 1997"
 .SH NAME
-rexec -- remote execution client for an exec server
+rexec \-\- remote execution client for an exec server
 .SH SYNOPSIS
 .B rexec 
 [  
-.B \-a \-c \-d \-h \-n \-s
-] [--] host command
+.B \-abcdhns \-l 
+username 
+.B \-p 
+password
+] host command
 .SH DESCRIPTION
 .B Rexec
 calls the 
@@ -54,87 +57,123 @@
 for details of the protocol.
 .SH OPTIONS
 .B Rexec
-accepts the following options:
+accepts several options, but only three are likely to be very useful:
 .\"
 .LP
-\fB\-a\fP
+\fB\-l username\fP
 .IP
+Set the log-in name on the remote host to username.
+.\"
+.LP
+\fB\-p password\fP
+.IP
+Provide the password for the remote account.  The command line argument
+will be blanked after being parsed, to prevent it from being
+seen with 
+.B ps(1).
+However, it is still not very secure to type the password on the 
+command line.  In particular, be sure that the shell's history file
+is protected.
+.TP
+\fB\-n\fP
+Explicitly prompt for name and password, even if provided in
+the environment, in the $HOME/.netrc file, or in the environmental
+variables REXEC_USER and REXEC_PASS.
+.PP
+Other options that might be useful with non-standard remote exec
+daemons, or to debug connections:
+.TP
+\fB\-a\fP
 Do not set up an auxiliary channel for standard error from command;
 the remote standard error and output are then both returned on the
 local standard output.  By default, 
 .B rexec
 asks that a separate channel be set up for diagnostic output
 from the remote command.
-.\"
-.LP
+.TP
+\fB\-b\fP
+Use signal handling as in BSD rsh(1).  Only the signals 
+SIGINT, SIGQUIT, and SIGTERM are echoed to the remote process.
+They do not remain raised locally, so rexec waits for the
+remote command to shutdown its side of the socket.  Also, CNTRL-Z will
+only suspend execution locally--the remote command may continue to run.
+.TP
 \fB\-c\fP
-.IP
 Do not close remote standard input when local standard input closes.
 Normally the standard input to the remote command is closed when
 the local standard input is closed.
-.\"
-.LP
+.TP
 \fB\-d\fP
-.IP
 Turn on debugging information. In particular the command sent to the
 remote host will be echoed.
-.\"
-.LP
+.TP
 \fB\-h\fP
-.IP
 Print a usage message.
-.\"
-.LP
-\fB\-n\fP
-.IP
-Explicitly prompt for name and password.  Otherwise,
-$HOME/.netrc will be scanned for login information.
-.\"
-.LP
+.TP
 \fB\-s\fP
-.IP
 Do not echo signals received by the rexec onto the remote
 process.  Normally, signals which can be trapped are passed
 on to the remote process; then, when you type CNTRL-C, the remote
 process terminates as well.
-.\"
-.LP
-\fB\--\fP
+.SH USERNAME AND PASSWORD
+.B Rexec(1)
+searches for the username and password in the following order:
 .IP
-Signals end of options to
-.B rexec
-to allow option switches in ``command.''
-.PP
-The only option that is very useful is -n; 
-even then, if you have not
-set up a password in $HOME/.netrc, you should still be prompted.
+1. If -n is given on the command line, the user will always be
+prompted for both, even if they are also given on the command line.
+.IP
+2. The command line will be parsed
+.IP
+3. If the environmental variables REXEC_USER or REXEC_PASS are
+defined, they will define the username or password.
+.IP
+4. The $HOME/.netrc file will be searched.  See
+.B ftp(1)
+for a description of this file's format.
+.IP
+5. Finally, the user will be prompted if either the username or password 
+remains undefined.
+
+.SH SECURITY
+Users of this command should be aware that 
+.B rexec(3)
+transmits their password to the remote host clear text, not
+encrypted.  If the network is not secure to the remote host, the
+password can be comprimised.
+
+.SH SIGNALS
+Without the -b option,
+all signals which can be handled are echoed to the remote process.
+Afterwards, however, they remain raised in the local process.  
+Typically, this means that 
+.B rexec(1)
+will exit after receiving a fatal signal, even if the remote
+process has arranged to handle or ignore it.
+
+Differing operating systems use differing signal numbers; for example
+AIX and SunOS use 18 for SIGTSTP (^Z), while Linux uses 20.  Therefore, 
+it may have a different effect remotely than
+locally.  In particular, typing CNTL-Z may not suspend the execution
+of the remote process.
 .SH EXAMPLE
 .PP
-rexec othermachine -- cat ">remote_file; date" <local_file
+rexec othermachine cat ">remote_file; date" <local_file
 .PP
 will send local_file to the othermachine as remote_file.
 
 .SH BUGS
-When a signal is received locally, the signal is passed to the remote
-command using
-blocking i/o.  Even though it is only one byte, this may result
-in delay in teminating the local command if communications are slow.
-Also, the signal remains raised in the local rexec procedure, so
-typically the remote command cannot ask for further user input
-after the signal is sent.
-.PP
-It is annoying to have to use the ``--'' flag to include options in
-the remote command without quoting them.  This is because the GNU
-getopt(3) routine is being called.  This behavior may be corrected 
-by setting the POSIXLY_CORRECT environtmental variable.
 .PP
-Please send bug reports or system incompatibilities to the author.
+Please send bug reports, system incompatibilities,
+and job offers to the author.
 .SH "SEE ALSO"
-rexec(3), rexecd(8)
+rexec(3), rexecd(8), rsh(1)
 .SH AUTHOR
 .PP
 Michael Sadd 
 .br
-sadd@msc.cornell.edu
+mas22@cornell.edu
 .br
-http://www.msc.cornell.edu/~sadd/
+http://www.tc.cornell.edu/~sadd/
+
+Thanks to Orange Gopher (2/10/97) and Johannes Plass
+(plass@dipmza.physik.uni-mainz.de, Oct. 17 1996) for useful suggestions.
--- rexec/rexec.c
+++ rexec/rexec.c
@@ -1,33 +1,36 @@
 
 /*
 
-  rexec.c -- Copyright 1996 by Michael Sadd (sadd@msc.cornell.edu)
+  rexec.c -- Copyright 1996 by Michael Sadd (mas22@cornell.edu)
 
-  Version 1.1  Sept. 12 1996
+  Version 1.5 Jul. 29, 1999
 
   Permission is given to freely distribute this program
   provided this source is included and this copyright notice and
   header remain.
 
-  This program calls the system rexec subroutine to 
+  This program calls the system rexec subroutine to
   act as a rexec client.
 
   Please report bugs and system incompatibilities to me.
 
   I have compiled this under Linux 2.0.18, libc5.3.12 and gcc2.7.2, as well
   as under AIX 3.2 and 4.1 using gcc.  It is meant for Linux, however,
-  and should be portable to other systems.  Please let me know if there
+  but should be portable to other systems.  Please let me know if there
   are problems.
-  
-  Usage: rexec [ -a -c -d -h -n -s ] [--] host command
+
+  Usage: rexec [ -acdhns ] -l username -p password host command
+     -l username: Sets the login name for the remote host.
+     -p password: Sets the password for the remote host.
+     -n: Explicitly prompt for name and password.  Otherwise,
+         $HOME/.netrc will be scanned for login information.
      -a: Do not set up an auxiliary channel for standard error from command;
          the remote standard error and output are then both returned on the
          local standard output.
+     -b: Use BSD rsh style signal handling.
      -c: Do not close remote standard input when local standard input closes.
      -d: Turn on debugging information.
      -h: Print this usage message.
-     -n: Explicitly prompt for name and password.  Otherwise,
-         $HOME/.netrc will be scanned for login information.
      -s: Do not echo signals received by the rexec onto the remote
          process.  Normally, signals which can be trapped are passed
          on to the remote process; then, when you type CNTRL-C, the remote
@@ -37,9 +40,9 @@
 
   Example:
 
-  ~/bin@athens% rexec othermachine -- cat ">remote_file; date" <local_file 
+  ~/bin@athens% rexec othermachine -- cat ">remote_file; date" <local_file
   Fri Sep 13 02:25:20 EDT 1996
-  
+
   Here my password and user name are set up in $HOME/.netrc.
 
   The only option that is very useful is -n; even then, if you haven't
@@ -47,18 +50,17 @@
 
   */
 
-#include "../version.h"
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <netinet/in.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <netdb.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <errno.h>
+#include <netinet/in.h>
 #include <pwd.h>
 #include <signal.h>
 #ifndef FD_SETSIZE
@@ -69,7 +71,7 @@
 #define MAX_PORT IPPORT_RESERVED
 #define EXTRA_PORT_LOW IPPORT_USERRESERVED
 #define EXTRA_PORT_MAX (EXTRA_PORT_LOW + 10000)
-#define BUFLEN 512
+#define BUFLEN BUFSIZ
 #ifndef EAGAIN
 #ifdef EWOULDBLOCK
 #define EAGAIN EWOULDBLOCK
@@ -79,27 +81,37 @@
 #endif
 #ifdef _PASSWORD_LEN
 #define USERLEN _PASSWORD_LEN
-#else 
+#else
 #define USERLEN 256
 #endif
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+typedef void (*SIGHANDLER_T)(int);
 
 void parse_options(char *argv[], int argc, int *debug, int *extra_error,
 		   int *close_on_stdin, int *prompt, int *pass_sig,
-		   char **host, char **command);
+		   char **host, char **command, char **user_name,
+		   char **passwd);
 void usage(char *name);
 int echo_fd(int fd_to, int fd_from, char *prog_name, int debug);
-void set_signals(void);
+void set_signal(int sig, SIGHANDLER_T handler);
 void echo_sig(int sig);
+void safe_write_error(const char *message);
 
 /* These need to be global for signal passing. */
-int aux_sock; /* Socket for auxiliary channel. */
+int aux_sock=-1; /* Socket for auxiliary channel. */
 int extra_error = 1; /* Setup special channel for standard error? */
+int bsd = 0; /* BSD compatible signals? */
+int bsd_signals[] = {SIGINT,SIGQUIT,SIGTERM};
+int debug = 0;  /* Turn on debugging info? */
+char *progname;
 
 int main(int argc, char *argv[])
 {
-  
+
   /* Program options and parameters. */
-  int debug = 0;  /* Turn on debugging info? */
   int prompt = 0; /* Prompt for name and password? */
   int close_on_stdin = 1; /* Close socket on stdin, not on remote host. */
   int pass_sig = 1; /* Should we pass signals to the remote process? */
@@ -112,9 +124,14 @@
   int sock; /* Rexec socket. */
   int *p_to_aux_sock; /* Pointer to socket for auxiliary channel. */
   int sock_open, stdin_open, aux_open, shut_down; /* Open file descriptor flags. */
+  struct addrinfo hints, *res;
+  int gaierr;
+  sa_family_t af = AF_INET;
 
+
+  progname=argv[0];
   parse_options(argv, argc, &debug, &extra_error, &close_on_stdin, &prompt,
-		&pass_sig, &host, &command);
+		&pass_sig, &host, &command, &user_name, &passwd);
 
   service = getservbyname("exec","tcp");
   if ( (port_exec = service->s_port) >= MAX_PORT )
@@ -127,79 +144,29 @@
     }
     port_exec = htons(DEFAULT_PORT);
   }
-  
-  if ( extra_error )
-  {
-    int port_extra; /* Auxiliary port number. */
-    struct sockaddr_in aux_name; /* Auxilliary socket name for bind. */
-    int found_port = 0;
 
-    if (debug)
-      fprintf(stderr,"%s: Attempting to allocate channel for remote "
-	      "standard error\n", argv[0]);
-    
-    if ( (aux_sock = socket(PF_INET, SOCK_STREAM , 0)) < 0)
-    {
-      fprintf(stderr,"%s: Error in socket call: ",argv[0]);
-      perror(NULL);
-      exit(1);
-    }
-
-    aux_name.sin_family = AF_INET;
-    aux_name.sin_addr.s_addr = htonl(INADDR_ANY);
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_flags = AI_CANONNAME;
+  hints.ai_socktype = SOCK_STREAM;
+  if ((gaierr = getaddrinfo(host, NULL, &hints, &res))) {
+    fprintf(stderr, "%s: Couldn't look up address for %s: %s",
+            argv[0], host, gai_strerror(gaierr));
+    exit(1);
+  }
+  af = res->ai_family;
+  freeaddrinfo(res);
 
-    for ( port_extra = EXTRA_PORT_LOW; port_extra < EXTRA_PORT_MAX; 
-	  ++port_extra)
-    {
-      aux_name.sin_port = htons(port_extra);
-      found_port = (bind(aux_sock, (struct sockaddr *) &aux_name, 
-			 sizeof(aux_name)) == 0);
-      if ( found_port )
-	break;
-      switch (errno)
-      {
-      case EADDRNOTAVAIL:
-      case EADDRINUSE:
-      case EACCES:
-	if (debug) 
-	{
-	  fprintf(stderr,"%s: Error from bind for port No. %d: ",argv[0],
-		  port_extra);
-	  perror(NULL);
-	  fprintf(stderr,"Will try next port...\n");
-	}
-	break;
-      default:
-	fprintf(stderr,"%s: Error binding to socket for aux. channel: ",
-		argv[0]);
-	perror(NULL);
-	exit(1);
-	break;
-      }
-    }
-    
-    if ( ! found_port )
-    {
-      fprintf(stderr,"%s: Could not find available port for auxiliary ",
-	      argv[0]);
-      fprintf(stderr,"channel in port range %d to %d.\n",EXTRA_PORT_LOW,
-	      EXTRA_PORT_MAX);
-      exit(1);
-    }
+  /* Non-zero  p_to_aux_sock is signal to rexec(3) to establish
+     auxilliary socket for standard error and signal passing. */
+  if ( extra_error )
     p_to_aux_sock = &aux_sock;
-    /* listen here? */
-
-    if (pass_sig)
-      set_signals();
-
-  }
   else /* else we just want standard error directed as standar out--no aux */
     p_to_aux_sock = NULL;
 
-  if (prompt)
+  if (prompt || user_name == NULL || prompt == NULL)
   {
     FILE *term_in,*term_out;
-    
+
     if ( ( term_in = fopen("/dev/tty","r+")) == NULL)
     {
       term_in = stdin;
@@ -207,21 +174,55 @@
     }
     else
       term_out = term_in;
-    fprintf(term_out,"Username at %s: ",host);
-    user_name = fgets(user_buf,USERLEN,term_in);
-    user_name[strlen(user_name)-1] = '\0'; /* Hopefully fgets always adds
-					      a newline. */
-    passwd = getpass("Password: ");
+
+    if (prompt || user_name == NULL)
+      {
+	fprintf(term_out,"Username at %s: ",host);
+	user_name = fgets(user_buf,USERLEN,term_in);
+	user_name[strlen(user_name)-1] = '\0'; /* Hopefully fgets always adds
+						  a newline. */
+      }
+    if (prompt || passwd == NULL)
+      passwd = getpass("Password: ");
   }
 
-  if ( (sock = rexec(&host, port_exec, user_name, passwd, command, 
-		     p_to_aux_sock)) < 0 )
- {
-    fprintf(stderr,"%s: Error in rexec system call: ",argv[0]);
-    perror(NULL);
+  if ( user_name == NULL )
+    user_name = getenv("REXEC_USER");
+  if ( passwd == NULL )
+    passwd = getenv("REXEC_PASS");
+
+  if ( (sock = rexec_af (&host, port_exec, user_name, passwd, command,
+		     p_to_aux_sock, af)) < 0 )
+  {
+    fprintf(stderr,"%s: Error in rexec system call,\n",argv[0]);
+    fprintf(stderr,"%s: (The following system error may itself be in error)\n",argv[0]);
+    perror(argv[0]);
     exit(1);
   }
 
+  /* Check to see if auxilliary socket really set. */
+  if ( extra_error && ( p_to_aux_sock == NULL ) ) {
+    extra_error = 0;
+    if (debug) {
+      fprintf(stderr,"%s: Separate channel for standard error not opened.\n",
+              progname);
+    }
+  }
+
+  if ( pass_sig && p_to_aux_sock )
+  {
+    int sig;
+
+    if (bsd)
+    {
+      for (sig = 0; sig < sizeof(bsd_signals)/sizeof(int); ++sig)
+	set_signal(bsd_signals[sig],echo_sig);
+    } else {
+      for (sig = 1; sig < NSIG; ++sig)
+	set_signal(sig,echo_sig);
+    }
+  }
+
   sock_open = stdin_open = aux_open = 1;
   shut_down = 0;
   while (sock_open || ( aux_open && extra_error )) /* echo stdin -> remote host
@@ -229,6 +230,7 @@
 	                           until the remote host closes the socket. */
   {
     fd_set read_set;
+    int select_ret;
 
     FD_ZERO(&read_set);
     if (stdin_open)
@@ -239,8 +241,13 @@
       FD_SET(aux_sock, &read_set);
 
 
-    /* Using an infinit timeout in select (last parameter = NULL). */
-    if ( select(FD_SETSIZE, &read_set, NULL, NULL, NULL) < 0 )
+    /* Using an infinite timeout in select (last parameter = NULL). */
+    do {
+      errno =0;
+      select_ret=select(FD_SETSIZE, &read_set, NULL, NULL, NULL);
+    } while ( errno == EINTR );
+
+    if ( select_ret < 0 )
     {
       fprintf(stderr,"%s: Error in select system call: ",argv[0]);
       perror(NULL);
@@ -256,7 +263,7 @@
     if ( extra_error && FD_ISSET(aux_sock, &read_set) )
       aux_open = echo_fd(STDERR_FILENO, aux_sock, argv[0], debug);
 
-    if ( ! stdin_open && close_on_stdin && ! shut_down ) 
+    if ( ! stdin_open && close_on_stdin && ! shut_down )
     {
       if (shutdown(sock, 1) <0)
       {
@@ -273,11 +280,14 @@
 }
 
 
-#define OPTIONS "dahncs"
+#define OPTIONS "+dahncbsl:p:"
+extern char *optarg;
+extern int optopt;
 
 void parse_options(char *argv[], int argc, int *debug, int *extra_error,
 		   int *close_on_stdin, int *prompt, int *pass_sig,
-		   char **host, char **command)
+		   char **host, char **command, char **user_name,
+		   char **passwd)
 {
   int opt;
   int len = 0,ind;
@@ -285,6 +295,20 @@
   while ((opt = getopt(argc, argv, OPTIONS)) > 0)
     switch (opt)
     {
+    case 'l':
+      if (optarg != NULL)
+	*user_name=strcpy((char *)malloc((strlen(optarg)+1)*sizeof(char)),optarg);
+      break;
+    case 'p':
+      if ( optarg!= NULL )
+      {
+        int passlen = strlen(optarg);
+
+	*passwd=strcpy((char *)malloc((passlen+1)*sizeof(char)),optarg);
+        for (ind = 0; ind < passlen; ++ind)
+          optarg[ind] = '\0';
+      }
+      break;
     case 'd':
       *debug = 1;
       break;
@@ -300,11 +324,14 @@
     case 'c':
       *close_on_stdin = 0;
       break;
+    case 'b':
+      bsd = 1;
+      break;
     case 's':
       *pass_sig = 0;
       break;
     default:
-      /* fprintf(stderr,"%s: Unknown option -%c\n",argv[0],(char)optopt);*/
+      /*fprintf(stderr,"%s: Unknown option -%c\n",argv[0],(char)optopt);*/
       usage(argv[0]);
       break;
     }
@@ -314,12 +341,25 @@
     usage(argv[0]);
   }
   *host = argv[optind++];
+  if ( strcmp(argv[optind],"-l" ) == 0 )
+  {
+    if ( (optind += 2 ) >= argc )
+    {
+      fprintf(stderr,"%s: Require a remote command to execute.\n",argv[0]);
+      usage(argv[0]);
+    }
+    *user_name=argv[optind-1];
+  }
   for ( ind = optind; ind < argc; ++ind)
     len += strlen(argv[ind])+1;
   *command = (char *) malloc((len+1)*sizeof(char));
   **command = '\0';
-  for ( ind = optind; (ind < argc) && strcat(*command," "); ++ind)
+  for ( ind = optind; ind < argc; ++ind) {
+    if ( ind > optind ) {
+      (void)strcat(*command," ");
+    }
     (void) strcat(*command, argv[ind]);
+  }
 
   if (*debug)
   {
@@ -327,17 +367,20 @@
     fprintf(stderr,"%s: Command to execute = %s\n", argv[0], *command);
   }
 }
-      
+
 void usage(char *name)
 {
-  fprintf(stderr,"Usage: %s [ -a -c -d -h -n ] [--] host command\n", name);
-  fprintf(stderr,"\t-a: Do not set up an auxiliary channel for standard error\n");
-  fprintf(stderr,"\t-c: Do not close remote standard in when local input closes\n");
-  fprintf(stderr,"\t-d: Turn on debugging information.\n");
-  fprintf(stderr,"\t-h: Print this usage message.\n");
-  fprintf(stderr,"\t-n: Explicitly prompt for name and password.\n");
-  fprintf(stderr,"\t-s: Do not echo signals to the remote process. \n");
-  fprintf(stderr,"\t--: Signals end of options to allow options in `command`\n");
+  fprintf(stderr,"Usage: %s [ -abcdhns ] -l username -p password  host command\n", name);
+  fprintf(stderr,
+	  "     -l username: Sets the login name for the remote host.\n"
+	  "     -p password: Sets the password for the remote host.\n"
+	  "     -n: Explicitly prompt for name and password.\n"
+	  "     -a: Do not set up an auxiliary channel for standard error.\n"
+	  "     -b: Use BSD-rsh type signal handling.\n"
+	  "     -c: Do not close remote standard in when local input closes\n"
+	  "     -d: Turn on debugging information.\n"
+	  "     -h: Print this usage message.\n"
+	  "     -s: Do not echo signals to the remote process. \n");
   exit(1);
 }
 
@@ -345,10 +388,8 @@
    zero on end of file. */
 int echo_fd(int fd_to, int fd_from, char *prog_name, int debug)
 {
-  int sock_read;
-  char buffer[BUFLEN];
-
-  (void)debug;
+  int sock_read, sock_send;
+  char buffer[BUFLEN],*write_buf;
 
   if ( (sock_read = read(fd_from, buffer, BUFLEN)) < 0 )
   {
@@ -356,38 +397,121 @@
     perror(NULL);
     exit(1);
   }
-  
-  if ( sock_read )
-    write(fd_to, buffer, sock_read);
+
+  for(write_buf=buffer,sock_send=sock_read; sock_send; )
+  {
+    int ch =  write(fd_to, write_buf, sock_send);
+    if (ch < 0 ) {
+      if ( errno == EAGAIN )
+	continue;
+      perror(progname);
+      exit(1);
+    }
+    sock_send -= ch;
+    write_buf += ch;
+    if (debug && sock_send)
+      fprintf(stderr,"%s: sent only %d of %d bytes received.\n",progname,ch,
+	      sock_read);
+  }
 
   return sock_read;
 }
 
 
-void set_signals(void)
+void set_signal(int sig, SIGHANDLER_T handler)
 {
-  int sig;
 
-  for (sig = 1; sig < NSIG; ++sig)
-    signal(sig, echo_sig);
+  switch (sig)
+  {
+    struct sigaction action;
+
+  case SIGKILL:
+  case SIGSTOP:
+    return;
+    break;
+  default:
+    if ( sigaction(sig, NULL, &action) < 0 )
+    {
+      perror(progname);
+      exit(1);
+    }
+    if ( action.sa_handler != SIG_IGN )
+    {
+      action.sa_handler = handler;
+      action.sa_flags = SA_RESTART;
+      (void) sigemptyset(&(action.sa_mask));
+      if ( sigaction(sig, &action, NULL) < 0 )
+      {
+	perror(progname);
+	exit(1);
+      }
+    }
+  }
+
 }
 
+
+#define SAFEMSGSIZ 512
+
 void echo_sig(int sig)
 {
   char sigch = (char) sig;
+  static char safe_msg[SAFEMSGSIZ];
+  int saved_errno = errno;
+
+  if (debug) {
+    sprintf(safe_msg,"rexec: echo_sig: received signal %d\n",sig);
+    safe_write_error(safe_msg);
+  }
+
+  if ( (aux_sock > 0) && (write(aux_sock, &sigch, 1) < 1) && debug ) {
+    sprintf(safe_msg,"Error: Signal %d was not properly sent to the remote "
+	    "process.\n",sig);
+    safe_write_error(safe_msg);
+  }
+
 
-  if (extra_error)
-    write(aux_sock, &sigch, 1);
+  if ( sig == SIGTSTP ) {
+    sprintf(safe_msg,"%s: The stop signal does not translate correctly to all"
+	    " remote hosts.\nDepending on the host, your remote process may "
+	    "still be running.\n",progname);
+    safe_write_error(safe_msg);
+  }
+
+
+  if ( ! bsd ) {
+    sigset_t sig_mask;
+
+    set_signal(sig,SIG_DFL);
+    (void)sigemptyset(&sig_mask);
+    (void)sigaddset(&sig_mask, sig);
+    if ( sigprocmask( SIG_UNBLOCK, &sig_mask, NULL) < 0 ) {
+      perror(progname);
+      exit(1);
+    }
+
+    kill(getpid(), sig);     /*    raise(sig), but more prortable. */
+
+    set_signal(sig, echo_sig);
+  }
+
+  errno = saved_errno;
 
-  raise(sig);
 }
 
+void safe_write_error(const char *message) {
+
+  int msg_len = strlen(message);
+  /* Ignore error condition--If this doesn't work, not much can be done. */
+  (void) write(STDERR_FILENO, message, msg_len);
+
+}
 
 /*
 void set_no_blocking(int fd)
 {
   int old_flag = fcntl(fd, F_GETFD, 0);
-  
+
   if (old_flag < 0)
   {
     perror("Error in fcntl(fd, F_GETFD, 0):");
@@ -401,3 +525,66 @@
 
 }
 */
+/*
+
+    int port_extra;
+    struct sockaddr_storage aux_name;
+    int found_port = 0;
+
+    if (debug)
+      fprintf(stderr,"%s: Attempting to allocate channel for remote "
+	      "standard error\n", argv[0]);
+
+    if ( (aux_sock = socket(af, SOCK_STREAM , 0)) < 0)
+    {
+      fprintf(stderr,"%s: Error in socket call: ",argv[0]);
+      perror(NULL);
+      exit(1);
+    }
+
+    memset(&aux_name, 0, sizeof(aux_name));
+    ((struct sockaddr *)&aux_name)->sa_family = af;
+
+    for ( port_extra = EXTRA_PORT_LOW; port_extra < EXTRA_PORT_MAX;
+	  ++port_extra)
+    {
+      if (af == AF_INET6)
+        ((struct sockaddr_in6 *)&aux_name)->sin6_port = htons(port_extra);
+      else
+        ((struct sockaddr_in *)&aux_name)->sin_port = htons(port_extra);
+      if ( found_port = (bind(aux_sock, (struct sockaddr *) &aux_name,
+		 sizeof(aux_name)) == 0) )
+	break;
+      switch (errno)
+      {
+      case EADDRNOTAVAIL:
+      case EADDRINUSE:
+      case EACCES:
+	if (debug)
+	{
+	  fprintf(stderr,"%s: Error from bind for port No. %d: ",argv[0],
+		  port_extra);
+	  perror(NULL);
+	  fprintf(stderr,"Will try next port...\n");
+	}
+	break;
+      default:
+	fprintf(stderr,"%s: Error binding to socket for aux. channel: ",
+		argv[0]);
+	perror(NULL);
+	exit(1);
+	break;
+      }
+    }
+
+    if ( ! found_port )
+    {
+      fprintf(stderr,"%s: Could not find available port for auxiliary ",
+	      argv[0]);
+      fprintf(stderr,"channel in port range %d to %d.\n",EXTRA_PORT_LOW,
+	      EXTRA_PORT_MAX);
+      exit(1);
+    }
+    p_to_aux_sock = &aux_sock;
+
+*/
--- rexec/rexec_af.c
+++ rexec/rexec_af.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1980, 1993
+ *	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.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rexec.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <alloca.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int	rexecoptions;
+char	ahostbuf[NI_MAXHOST];
+
+int
+rexec_af(ahost, rport, name, pass, cmd, fd2p, af)
+	char **ahost;
+	int rport;
+	const char *name, *pass, *cmd;
+	int *fd2p;
+	sa_family_t af;
+{
+	struct sockaddr_storage sa2, from;
+	struct addrinfo hints, *res0;
+	const char *orig_name = name;
+	const char *orig_pass = pass;
+	u_short port = 0;
+	int s, timo = 1, s3;
+	char c;
+	int gai;
+	char servbuff[NI_MAXSERV];
+
+	snprintf(servbuff, sizeof(servbuff), "%d", ntohs(rport));
+	servbuff[sizeof(servbuff) - 1] = '\0';
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = af;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_CANONNAME;
+	gai = getaddrinfo(*ahost, servbuff, &hints, &res0);
+	if (gai){
+		/* XXX: set errno? */
+		return -1;
+	}
+
+	if (res0->ai_canonname){
+		strncpy(ahostbuf, res0->ai_canonname, sizeof(ahostbuf));
+		ahostbuf[sizeof(ahostbuf)-1] = '\0';
+		*ahost = ahostbuf;
+	}
+	else{
+		*ahost = NULL;
+	}
+	ruserpass(res0->ai_canonname, &name, &pass);
+retry:
+	s = socket(res0->ai_family, res0->ai_socktype, 0);
+	if (s < 0) {
+		perror("rexec: socket");
+		return (-1);
+	}
+	if (connect(s, res0->ai_addr, res0->ai_addrlen) < 0) {
+		if (errno == ECONNREFUSED && timo <= 16) {
+			(void) close(s);
+			sleep(timo);
+			timo *= 2;
+			goto retry;
+		}
+		perror(res0->ai_canonname);
+		return (-1);
+	}
+	if (fd2p == 0) {
+		(void) write(s, "", 1);
+		port = 0;
+	} else {
+		char num[32];
+		int s2, sa2len;
+
+		s2 = socket(res0->ai_family, res0->ai_socktype, 0);
+		if (s2 < 0) {
+			(void) close(s);
+			return (-1);
+		}
+		listen(s2, 1);
+		sa2len = sizeof (sa2);
+		if (getsockname(s2, (struct sockaddr *)&sa2, &sa2len) < 0) {
+			perror("getsockname");
+			(void) close(s2);
+			goto bad;
+		}
+		port = 0;
+		if (!getnameinfo((struct sockaddr *)&sa2, sa2len,
+				 NULL, 0, servbuff, sizeof(servbuff),
+				 NI_NUMERICSERV))
+			port = atoi(servbuff);
+		(void) sprintf(num, "%u", port);
+		(void) write(s, num, strlen(num)+1);
+		{ int len = sizeof (from);
+		  s3 = accept(s2, (struct sockaddr *)&from, &len);
+		  close(s2);
+		  if (s3 < 0) {
+			perror("accept");
+			port = 0;
+			goto bad;
+		  }
+		}
+		*fd2p = s3;
+	}
+	(void) write(s, name, strlen(name) + 1);
+	/* should public key encypt the password here */
+	(void) write(s, pass, strlen(pass) + 1);
+	(void) write(s, cmd, strlen(cmd) + 1);
+
+	/* We don't need the memory allocated for the name and the password
+	   in ruserpass anymore.  */
+	if (name != orig_name)
+	  free ((char *) name);
+	if (pass != orig_pass)
+	  free ((char *) pass);
+
+	if (read(s, &c, 1) != 1) {
+		perror(*ahost);
+		goto bad;
+	}
+	if (c != 0) {
+		while (read(s, &c, 1) == 1) {
+			(void) write(2, &c, 1);
+			if (c == '\n')
+				break;
+		}
+		goto bad;
+	}
+	freeaddrinfo(res0);
+	return (s);
+bad:
+	if (port)
+		(void) close(*fd2p);
+	(void) close(s);
+	freeaddrinfo(res0);
+	return (-1);
+}
+
+int
+rexec(ahost, rport, name, pass, cmd, fd2p)
+	char **ahost;
+	int rport;
+	const char *name, *pass, *cmd;
+	int *fd2p;
+{
+	return rexec_af(ahost, rport, name, pass, cmd, fd2p, AF_INET);
+}
--- rexecd/Makefile
+++ rexecd/Makefile
@@ -31,11 +31,7 @@
 	install -m$(MANMODE) rexecd.8 $(INSTALLROOT)$(MANDIR)/man8/in.rexecd.8
 	ln -sf in.rexecd.8 $(INSTALLROOT)$(MANDIR)/man8/rexecd.8
 ifeq ($(USE_PAM),1)
-	@echo
-	@echo You have chosen to use PAM as the authentication method
-	@echo You should copy the rexec.pam file provided to /etc/pam.d
-	@echo or if you know what you are doing, tailor it to your needs
-	@echo
+	install -m644 rexec.pam $(INSTALLROOT)/etc/pam.d/rexec
 endif	
 
 clean:
--- rexecd/rexec.pam
+++ rexecd/rexec.pam
@@ -1,8 +1,7 @@
 #%PAM-1.0
-#auth       required    /lib/security/pam_securetty.so
-auth       required     /lib/security/pam_pwdb.so shadow nullok
-auth       required     /lib/security/pam_nologin.so
-auth       required     /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/ftpusers
-auth       required     /lib/security/pam_shells.so
-account    required     /lib/security/pam_pwdb.so
-
+auth     requisite      pam_nologin.so
+auth     include        common-auth
+account  include        common-account
+password include        common-password 
+session  required       pam_loginuid.so
+session  include        common-session
--- rexecd/rexecd.c
+++ rexecd/rexecd.c
@@ -32,7 +32,7 @@
  *
  *
  * 1-14-99 Karl R. Hakimian <hakimian@eecs.wsu.edu>
- * 
+ *
  * While the headers in this file claim only the purest decent from
  * their BSD roots, this program has had unspeakable things done to it
  * over the years. I have tried to clean things up and get them working
@@ -112,7 +112,7 @@
  */
 
 static void fatal(const char *);
-static void doit(struct sockaddr_in *fromp);
+static void doit(struct sockaddr *fromp);
 static void getstr(char *buf, int cnt, const char *err);
 
 static const char *remote = NULL;
@@ -120,17 +120,23 @@
 int
 main(int argc, char **argv)
 {
-	struct sockaddr_in from;
+	struct sockaddr_storage from;
 	socklen_t fromlen;
 
 	(void)argc;
 
 	fromlen = sizeof(from);
- 
+
 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
 		fprintf(stderr, "rexecd: getpeername: %s\n", strerror(errno));
 		return 1;
 	}
+	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
+	  IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
+		((struct sockaddr *)&from)->sa_family = AF_INET;
+		((struct sockaddr_in *)&from)->sin_addr.s_addr =
+		  ((struct sockaddr_in6 *)&from)->sin6_addr.s6_addr32[3];  
+	}
 
 	openlog(argv[0], LOG_PID, LOG_DAEMON);
 
@@ -142,19 +148,19 @@
 	remote = hosts_info(&from_host);
 #else
 	{
-	struct hostent *h = gethostbyaddr((const char *)&from.sin_addr,
-					  sizeof(struct in_addr),
-					  AF_INET);
-	if (!h || !h->h_name) {
+	char hbuf[NI_MAXHOST];
+
+	if (getnameinfo((struct sockaddr *)&from, fromlen,
+			hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD)) {
 		write(0, "\1Where are you?\n", 16);
 		return 1;
 	}
 	/* Be advised that this may be utter nonsense. */
-	remote = strdup(h->h_name);
+	remote = strdup(hbuf);
 	}
 #endif
 	syslog(allow_severity, "connect from %.128s", remote);
-	doit(&from);
+	doit((struct sockaddr *)&from);
 	return 0;
 }
 
@@ -221,7 +227,7 @@
 
 
 static void
-doit(struct sockaddr_in *fromp)
+doit(struct sockaddr *fromp)
 {
 	char cmdbuf[ARG_MAX+1];
 	char user[16], pass[16];
@@ -273,7 +279,7 @@
  We must connect back to the client here if a port was provided. KRH
 */
 	if (port != 0) {
-		s = socket(AF_INET, SOCK_STREAM, 0);
+		s = socket(fromp->sa_family, SOCK_STREAM, 0);
 		if (s < 0)
 			exit(1);
 
@@ -283,8 +289,11 @@
 			exit(1);
 #endif
 		alarm(60);
-		fromp->sin_port = htons(port);
-		if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0)
+		if (fromp->sa_family == AF_INET6)
+			((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
+		else
+			((struct sockaddr_in *)fromp)->sin_port = htons(port);
+		if (connect(s, fromp, sizeof(struct sockaddr_storage)) < 0)
 			exit(1);
 		alarm(0);
 	}
@@ -300,6 +309,7 @@
        PAM_password = pass;
        pam_error = pam_start("rexec", PAM_username, &PAM_conversation,&pamh);
        PAM_BAIL;
+       pam_set_item (pamh, PAM_TTY, "tty");
        pam_error = pam_authenticate(pamh, 0);
        PAM_BAIL;
        pam_error = pam_acct_mgmt(pamh, 0);
@@ -420,7 +430,7 @@
 	else cp2 = theshell;
 
 	/*
-	 * Close all fds, in case libc has left fun stuff like 
+	 * Close all fds, in case libc has left fun stuff like
 	 * /etc/shadow open.
 	 */
 	for (ifd = getdtablesize()-1; ifd > 2; ifd--) close(ifd);
@@ -453,4 +463,3 @@
 		}
 	} while (c != 0);
 }
-
--- rlogin/Makefile
+++ rlogin/Makefile
@@ -12,6 +12,7 @@
 install: $(PROG)
 	install -s -o root -m$(SUIDMODE) $(PROG) $(INSTALLROOT)$(BINDIR)
 	install -m $(MANMODE) $(PROG).1 $(INSTALLROOT)$(MANDIR)/man1
+	install -m $(MANMODE) rhosts.5 $(INSTALLROOT)$(MANDIR)/man5
 
 clean:
 	rm -f *.o $(PROG)
--- rlogin/rhosts.5
+++ rlogin/rhosts.5
@@ -0,0 +1,30 @@
+.\" Copyright (c) 1995 Peter Tobias <tobias@et-inf.fho-emden.de>
+.\" This file may be distributed under the GNU General Public License.
+.TH RHOSTS 5 "29 Jan 1995" "Linux" "Linux Programmer's Manual"
+.SH NAME
+$HOME/.rhosts \- grants or denies password-free \fBr\fP-command access
+to a specific user account
+.SH DESCRIPTION
+The \fB.rhosts\fP file allows or denies a user who has an account on the
+local host to use the \fBr\fP-commands (e.g. \fBrlogin\fP, \fBrsh\fP or
+\fBrcp\fP) without supplying a password.
+.PP
+The file uses the following format:
+.TP
+\fIhostname\fP \fI[username]\fP
+.PP
+Such an entry grants password-free \fBr\fP-command access for the user with
+the login name \fIusername\fP from the remote host \fIhostname\fP.
+If no user name is specified, the user must have the same login name on
+the remote host and the local host. For security reasons you should always
+use the FQDN of the hostname and not the short hostname.
+.PP
+Netgroups can be specified by preceeding the netgroup by an @ sign.
+.PP
+The \fB.rhosts\fP file must have permissions 600 (that is, readable and
+writable only by the owner).
+.PP
+.SH FILES
+$HOME/.rhosts
+.SH "SEE ALSO"
+.BR hosts.equiv "(5), " rshd "(8), " rlogind (8)
--- rlogin/rlogin.c
+++ rlogin/rlogin.c
@@ -279,7 +279,8 @@
 	/* will use SIGUSR1 for window size hack, so hold it off */
 	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
 
-	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
+	rem = rcmd_af (&host, sp->s_port, pw->pw_name, user, term, 0,
+			PF_UNSPEC);
 
 	if (rem < 0) exit(1);
 
--- rlogind/Makefile
+++ rlogind/Makefile
@@ -3,27 +3,23 @@
 include ../MCONFIG
 include ../MRULES
 
-OBJS = rlogind.o network.o auth.o
-# logwtmp.o
+OBJS = rlogind.o network.o auth.o sockconv.o
 
-ifeq ($(USE_PAM),1)
-OBJS += sockconv.o
 CFLAGS += -DUSE_PAM
-LIBS += -ldl -lpam -lpam_misc
-endif
+LIBS += -ldl -lpam
+
+LIBS += -lutil $(LIBTERMCAP)
 
 rlogind: $(OBJS)
 	$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
 
-rlogind.o: pathnames.h logwtmp.h rlogind.h ../version.h
-logwtmp.o: logwtmp.h
-auth.o network.o: rlogind.h
+$(OBJS): pathnames.h logwtmp.h
 
 install: rlogind
 	install -s -m$(DAEMONMODE) rlogind $(INSTALLROOT)$(SBINDIR)/in.rlogind
 	install -m$(MANMODE) rlogind.8 $(INSTALLROOT)$(MANDIR)/man8/in.rlogind.8
 	ln -sf in.rlogind.8 $(INSTALLROOT)$(MANDIR)/man8/rlogind.8
+	install -m 644 rlogin.pam $(INSTALLROOT)/etc/pam.d/rlogin
 
 clean:
 	rm -f *.o rlogind
-
--- rlogind/auth.c
+++ rlogind/auth.c
@@ -31,12 +31,14 @@
  * SUCH DAMAGE.
  */
 
+#include <stdio.h>
 #include <sys/types.h>
 #include <pwd.h>
 
 #include "rlogind.h"
 
 #ifdef USE_PAM
+#include <grp.h>
 
 /*
  * Modifications for Linux-PAM: Al Longyear <longyear@netcom.com>
@@ -122,7 +124,7 @@
     pam_set_item(pamh, PAM_USER, localuser);
     pam_set_item(pamh, PAM_RUSER, remoteuser);
     pam_set_item(pamh, PAM_RHOST, host);
-    pam_set_item(pamh, PAM_TTY, "tty");   /* ? */
+    pam_set_item(pamh, PAM_TTY, "rlogin");   /* we don't have a tty yet! */
 	
     network_confirm();
     retval = attempt_auth();
@@ -158,18 +160,14 @@
     pwd = getpwnam(localuser);
     if (pwd==NULL) {
         syslog(LOG_ERR, "user returned by PAM does not exist\n");
-	/* don't print this - it tells people which accounts exist */
-	/*fprintf(stderr, "rlogind: internal error\n");*/
 	return -1;
     }
     if (setgid(pwd->pw_gid) != 0) {
         syslog(LOG_ERR, "cannot assume gid for user returned by PAM\n");
-	fprintf(stderr, "rlogind: internal error\n");
 	return -1;
     }
     if (initgroups(localuser, pwd->pw_gid) != 0) {
         syslog(LOG_ERR, "initgroups failed for user returned by PAM\n");
-	fprintf(stderr, "rlogind: internal error\n");
 	return -1;
     }
     retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
@@ -194,6 +192,7 @@
 #define _check_rhosts_file  __check_rhosts_file
 #endif
 extern int _check_rhosts_file;
+extern sa_family_t from_af;
 
 
 void auth_checkoptions(void) {}
@@ -236,7 +235,7 @@
 
     _check_rhosts_file = use_rhosts;
 
-    return ruserok(host, pwd->pw_uid==0, remoteuser, localuser);
+    return ruserok_af(host, pwd->pw_uid==0, remoteuser, localuser, from_af);
 }
 
 #endif /* PAM */
--- rlogind/network.c
+++ rlogind/network.c
@@ -88,44 +88,62 @@
 	return(0);
 }
 
+sa_family_t from_af;
+static socklen_t fromlen;
 
 static char *
-find_hostname(const struct sockaddr_in *fromp, int *hostokp)
+find_hostname(const struct sockaddr *fromp, int *hostokp)
 {
-	struct hostent *hop;
+	struct addrinfo hints, *res, *res0;
+	char naddr[NI_MAXHOST];
+	char raddr[NI_MAXHOST];
+	char hbuf[NI_MAXHOST];
 	char *hname;
 	int hostok = 0;
 
-	hop = gethostbyaddr((const char *)&fromp->sin_addr, 
-			    sizeof(struct in_addr), fromp->sin_family);
-	if (hop == NULL) {
-		hname = strdup(inet_ntoa(fromp->sin_addr));
+	if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0,
+			NI_NAMEREQD)) {
+		if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0,
+				NI_NUMERICHOST))
+			strcpy(hbuf, "???");
+		hname = strdup(hbuf);
 		hostok = 1;
 	} 
-	else if (check_all || local_domain(hop->h_name)) {
+	else if (check_all || local_domain(hbuf)) {
 		/*
 		 * If name returned by gethostbyaddr is in our domain,
 		 * attempt to verify that we haven't been fooled by someone
 		 * in a remote net; look up the name and check that this
 		 * address corresponds to the name.
 		 */
-		hname = strdup(hop->h_name);
-		hop = gethostbyname(hname);
-		if (hop) {
-		    for (; hop->h_addr_list[0]; hop->h_addr_list++) {
-			if (!memcmp(hop->h_addr_list[0], &fromp->sin_addr,
-				    sizeof(fromp->sin_addr))) {
+		hname = strdup(hbuf);
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_family = from_af;
+		hints.ai_socktype = SOCK_STREAM;
+		hints.ai_flags = AI_CANONNAME;
+		if (getaddrinfo(hbuf, NULL, &hints, &res0) == 0) {
+		    if (getnameinfo(fromp, fromlen, naddr, sizeof(naddr),
+					NULL, 0, NI_NUMERICHOST))
+			strcpy(naddr, "???");
+		    for (res = res0; res; res = res->ai_next) {
+			if (res->ai_family != from_af)
+				continue;
+			if (getnameinfo(res->ai_addr, res->ai_addrlen,
+					raddr, sizeof(raddr), NULL, 0,
+					NI_NUMERICHOST) == 0
+			    && strcmp(naddr, raddr) == 0) {
+				free(hname);
+				hname = strdup(res->ai_canonname
+						? res->ai_canonname : hbuf);
 				hostok = 1;
 				break;
 			}
 		    }
-		    /* not clear if this is worthwhile */
-		    free(hname);
-		    hname = strdup(hop->h_name);
+		    freeaddrinfo(res0);
 		}
 	} 
 	else {
-		hname = strdup(hop->h_name);
+		hname = strdup(hbuf);
 		hostok = 1;
 	}
 
@@ -145,29 +163,38 @@
 char * 
 network_init(int f, int *hostokp)
 {
-	struct sockaddr_in from, *fromp;
-	socklen_t fromlen;
+	struct sockaddr_storage from;
+	struct sockaddr *fromp;
 	int on = 1;
 	char c;
 	char *hname;
 	int port;
 
-	from.sin_family = AF_INET;
 	fromlen = sizeof (from);
 	if (getpeername(f, (struct sockaddr *)&from, &fromlen) < 0) {
 		syslog(LOG_ERR,"Can't get peer name of remote host: %m");
 		fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
 	}
+	fromp = (struct sockaddr *)&from;
+	if (fromp->sa_family == AF_INET6 &&
+	  IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)fromp)->sin6_addr)) {
+		fromp->sa_family = AF_INET;
+		((struct sockaddr_in *)fromp)->sin_addr.s_addr =
+		  ((struct sockaddr_in6 *)fromp)->sin6_addr.s6_addr32[3];
+	}
+	from_af = fromp->sa_family;
 	if (keepalive &&
 	    setsockopt(f, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
 #ifdef IP_TOS
+	if (from_af == AF_INET)
+	{
 	#define IPTOS_LOWDELAY          0x10
 	on = IPTOS_LOWDELAY;
 	if (setsockopt(f, IPPROTO_IP, IP_TOS, &on, sizeof(on)) < 0)
 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+	}
 #endif
-	fromp = &from;
 
 	alarm(60);
 	read(f, &c, 1);
@@ -178,16 +205,24 @@
 	alarm(0);
 
 	hname = find_hostname(fromp, hostokp);
-
-	port = ntohs(fromp->sin_port);
-	if (fromp->sin_family != AF_INET ||
+	if (from_af == AF_INET6)
+		port = ntohs(((struct sockaddr_in6 *)fromp)->sin6_port);
+	else
+		port = ntohs(((struct sockaddr_in *)fromp)->sin_port);
+	if ((from_af != AF_INET && from_af != AF_INET6) ||
 	    port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) {
+	    char hbuf[MAXHOSTNAMELEN];
+
+	    if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0,
+				NI_NUMERICHOST))
+		strcpy(hbuf, "???");
 	    syslog(LOG_NOTICE, "Connection from %s on illegal port",
-		   inet_ntoa(fromp->sin_addr));
+				hbuf);
 	    fatal(f, "Permission denied", 0);
 	}
 
 #ifdef IP_OPTIONS
+	if (from_af == AF_INET)
 	{
 	    u_char optbuf[BUFSIZ/3], *cp;
 	    char lbuf[BUFSIZ];
--- rlogind/rlogin.pam
+++ rlogind/rlogin.pam
@@ -0,0 +1,10 @@
+#%PAM-1.0
+auth     requisite      pam_nologin.so
+auth     [user_unknown=ignore success=ok ignore=ignore auth_err=die default=bad] pam_securetty.so
+auth     sufficient     pam_rhosts.so
+auth     include        common-auth
+auth     required       pam_mail.so
+account  include        common-account
+password include        common-password
+session  required       pam_loginuid.so
+session  include        common-session
--- rsh/rsh.c
+++ rsh/rsh.c
@@ -163,7 +163,8 @@
 		exit(1);
 	}
 
-	rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
+	rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2,
+		      PF_UNSPEC);
 
 	if (rem < 0)
 		exit(1);
--- rshd/Makefile
+++ rshd/Makefile
@@ -6,9 +6,8 @@
 OBJS = rshd.o
 
 ifeq ($(USE_PAM),1)
-# ?
-CFLAGS += # -DUSE_PAM
-LIBS += -ldl # -lpam -lpam_misc
+CFLAGS += -DUSE_PAM
+LIBS += -ldl -lpam -lpam_misc
 endif
 
 rshd: $(OBJS)
@@ -18,6 +17,7 @@
 	install -s -m$(DAEMONMODE) rshd $(INSTALLROOT)$(SBINDIR)/in.rshd
 	install -m$(MANMODE) rshd.8 $(INSTALLROOT)$(MANDIR)/man8/in.rshd.8
 	ln -sf in.rshd.8 $(INSTALLROOT)$(MANDIR)/man8/rshd.8
+	install -m644 rsh.pam $(INSTALLROOT)/etc/pam.d/rsh
 
 clean:
 	rm -f *.o rshd
--- rshd/rsh.pam
+++ rshd/rsh.pam
@@ -0,0 +1,7 @@
+#%PAM-1.0
+auth     required       pam_rhosts.so
+auth     required       pam_nologin.so
+account  include        common-account 
+password include        common-password
+session  required       pam_loginuid.so
+session  include        common-session
--- rshd/rshd.c
+++ rshd/rshd.c
@@ -97,6 +97,8 @@
 static int paranoid = 0;
 static int sent_null;
 static int allow_root_rhosts=0;
+static sa_family_t from_af;
+static socklen_t fromlen;
 
 char	username[20] = "USER=";
 char	homedir[64] = "HOME=";
@@ -107,7 +109,7 @@
 extern	char	**environ;
 
 static void error(const char *fmt, ...);
-static void doit(struct sockaddr_in *fromp);
+static void doit(struct sockaddr *fromp);
 static void getstr(char *buf, int cnt, const char *err);
 
 extern int _check_rhosts_file;
@@ -275,26 +277,32 @@
     return pwd;
 #else
     if (pwd->pw_uid==0 && !allow_root_rhosts) return NULL;
-    if (ruserok(hostname, pwd->pw_uid==0, remuser, locuser) < 0) {
+    if (ruserok_af(hostname, pwd->pw_uid==0, remuser, locuser, from_af) < 0) {
 	return NULL;
     }
     return pwd;
 #endif
 }
 
-static const char *findhostname(struct sockaddr_in *fromp,
+static const char *findhostname(struct sockaddr *fromp,
 				const char *remuser, const char *locuser,
-				const char *cmdbuf) 
+				const char *cmdbuf)
 {
-	struct hostent *hp;
 	const char *hostname;
-
-	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
-			   fromp->sin_family);
+	char hbuf[NI_MAXHOST];
+	char naddr[NI_MAXHOST];
+	char raddr[NI_MAXHOST];
+	struct addrinfo hints, *res, *res0;
+	int gaierr;
+
+	if ((gaierr = getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf),
+					NULL, 0, 0))) {
+		error("getnameinfo: %s\n", gai_strerror(gaierr));
+		exit(1);
+	}
 
 	errno = ENOMEM; /* malloc (thus strdup) may not set it */
-	if (hp) hostname = strdup(hp->h_name);
-	else hostname = strdup(inet_ntoa(fromp->sin_addr));
+	hostname = strdup(hbuf);	/* not free */
 
 	if (hostname==NULL) {
 	    /* out of memory? */
@@ -302,34 +310,44 @@
 	    exit(1);
 	}
 
-	/*
-	 * Attempt to confirm the DNS. 
-	 */
-#ifdef	RES_DNSRCH
-	_res.options &= ~RES_DNSRCH;
-#endif
-	hp = gethostbyname(hostname);
-	if (hp == NULL) {
-	    syslog(LOG_INFO, "Couldn't look up address for %s", hostname);
-	    fail("Couldn't get address for your host (%s)\n", 
-		 remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf);
-	} 
-	while (hp->h_addr_list[0] != NULL) {
-	    if (!memcmp(hp->h_addr_list[0], &fromp->sin_addr,
-			sizeof(fromp->sin_addr))) {
-		return hostname;
-	    }
-	    hp->h_addr_list++;
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = from_af;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_CANONNAME;
+	if ((gaierr = getaddrinfo(hbuf, NULL, &hints, &res0))) {
+		syslog(LOG_INFO, "Couldn't look up address for %s: %s",
+			hbuf, gai_strerror(gaierr));
+		fail("Couldn't get address for your host (%s)\n",
+			remuser, hbuf, locuser, cmdbuf);
+	} else {
+		if (getnameinfo(fromp, fromlen, naddr, sizeof(naddr),
+				NULL, 0, NI_NUMERICHOST))
+			strcpy(naddr, "???");
+		for (res = res0; res; res = res->ai_next) {
+			if (res->ai_family != from_af)
+				continue;
+			if (getnameinfo(res->ai_addr, res->ai_addrlen,
+					raddr, sizeof(raddr), NULL, 0,
+					NI_NUMERICHOST) == 0
+			    && strcmp(naddr, raddr) == 0) {
+				break;	/* match */
+			}
+		}
+		if (res) {
+			freeaddrinfo(res0);
+			return hostname;
+		}
 	}
 	syslog(LOG_NOTICE, "Host addr %s not listed for host %s",
-	       inet_ntoa(fromp->sin_addr), hp->h_name);
-	fail("Host address mismatch for %s\n", 
-	     remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf);
+		naddr, res0->ai_canonname ? res0->ai_canonname : "???");
+	freeaddrinfo(res0);
+	fail("Host address mismatch for %s\n",
+		remuser, naddr, locuser, cmdbuf);
 	return NULL; /* not reachable */
 }
 
 static void
-doit(struct sockaddr_in *fromp)
+doit(struct sockaddr *fromp)
 {
 	char cmdbuf[ARG_MAX+1];
 	const char *theshell, *shellname;
@@ -350,7 +368,7 @@
 
 	if (port != 0) {
 		int lport = IPPORT_RESERVED - 1;
-		sock = rresvport(&lport);
+		sock = rresvport_af(&lport, from_af);
 		if (sock < 0) {
 		    syslog(LOG_ERR, "can't get stderr port: %m");
 		    exit(1);
@@ -359,9 +377,11 @@
 		    syslog(LOG_ERR, "2nd port not reserved\n");
 		    exit(1);
 		}
-		fromp->sin_port = htons(port);
-		if (connect(sock, (struct sockaddr *)fromp,
-			    sizeof(*fromp)) < 0) {
+		if (from_af == AF_INET6)
+			((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
+		else
+			((struct sockaddr_in *)fromp)->sin_port = htons(port);
+		if (connect(sock, fromp, fromlen) < 0) {
 		    syslog(LOG_INFO, "connect second port: %m");
 		    exit(1);
 		}
@@ -384,7 +404,7 @@
 	setpwent();
 	pwd = doauth(remuser, hostname, locuser);
 	if (pwd == NULL) {
-		fail("Permission denied.\n", 
+		fail("Permission denied.\n",
 		     remuser, hostname, locuser, cmdbuf);
 	}
 
@@ -416,15 +436,15 @@
 			exit(1);
 		}
 		if (pid) {
-			close(0); 
+			close(0);
 			close(1);
-			close(2); 
+			close(2);
 			close(pv[1]);
 			stderr_parent(sock, pv[0], pid);
 			/* NOTREACHED */
 		}
 		setpgrp();
-		close(sock); 
+		close(sock);
 		close(pv[0]);
 		dup2(pv[1], 2);
 		close(pv[1]);
@@ -479,7 +499,7 @@
 	}
 
 	/*
-	 * Close all fds, in case libc has left fun stuff like 
+	 * Close all fds, in case libc has left fun stuff like
 	 * /etc/shadow open.
 	 */
 	for (ifd = getdtablesize()-1; ifd > 2; ifd--) close(ifd);
@@ -489,18 +509,24 @@
 	exit(1);
 }
 
-static void network_init(int fd, struct sockaddr_in *fromp)
+static void network_init(int fd, struct sockaddr *fromp)
 {
 	struct linger linger;
-	socklen_t fromlen;
 	int on=1;
 	int port;
 
-	fromlen = sizeof(*fromp);
+	fromlen = sizeof(struct sockaddr_storage);
 	if (getpeername(fd, (struct sockaddr *) fromp, &fromlen) < 0) {
 		syslog(LOG_ERR, "getpeername: %m");
 		_exit(1);
 	}
+	if (fromp->sa_family == AF_INET6 &&
+	  IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)fromp)->sin6_addr)) {
+		fromp->sa_family = AF_INET;
+		((struct sockaddr_in *)fromp)->sin_addr.s_addr =
+		  ((struct sockaddr_in6 *)fromp)->sin6_addr.s6_addr32[3];
+	}
+	from_af = fromp->sa_family;
 	if (keepalive &&
 	    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
 	    sizeof(on)) < 0)
@@ -511,12 +537,13 @@
 	    sizeof (linger)) < 0)
 		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
 
-	if (fromp->sin_family != AF_INET) {
+	if (from_af != AF_INET && from_af != AF_INET6) {
 	    syslog(LOG_ERR, "malformed \"from\" address (af %d)\n",
-		   fromp->sin_family);
+		   from_af);
 	    exit(1);
 	}
 #ifdef IP_OPTIONS
+	if (from_af == AF_INET)
       {
 	u_char optbuf[BUFSIZ/3], *cp;
 	char lbuf[BUFSIZ+1], *lp;
@@ -528,7 +555,7 @@
 		ipproto = ip->p_proto;
 	else
 		ipproto = IPPROTO_IP;
-	if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
+	if (!getsockopt(fd, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
 	    optsize != 0) {
 		lp = lbuf;
 
@@ -543,9 +570,9 @@
 		syslog(LOG_NOTICE,
 		       "Connection received from %s using IP options"
 		       " (ignored): %s",
-		       inet_ntoa(fromp->sin_addr), lbuf);
+		       inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr),lbuf);
 
-		if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
+		if (setsockopt(fd, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
 			syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
 			exit(1);
 		}
@@ -556,10 +583,18 @@
 	/*
 	 * Check originating port for validity.
 	 */
-	port = ntohs(fromp->sin_port);
+	if (from_af == AF_INET6)
+		port = ntohs(((struct sockaddr_in6 *)fromp)->sin6_port);
+	else
+		port = ntohs(((struct sockaddr_in *)fromp)->sin_port);
 	if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) {
+		char hbuf[MAXHOSTNAMELEN];
+
+		if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf),
+				NULL, 0, NI_NUMERICHOST))
+			strcpy(hbuf, "???");
 	    syslog(LOG_NOTICE|LOG_AUTH, "Connection from %s on illegal port",
-		   inet_ntoa(fromp->sin_addr));
+					hbuf);
 	    exit(1);
 	}
 }
@@ -568,7 +603,7 @@
 main(int argc, char *argv[])
 {
 	int ch;
-	struct sockaddr_in from;
+	struct sockaddr_storage from;
 	_check_rhosts_file=1;
 
 	openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
@@ -611,11 +646,7 @@
                                "pam_rhosts_auth in /etc/pam.conf");
 #endif /* USE_PAM */
 
-	network_init(0, &from);
-	doit(&from);
+	network_init(0, (struct sockaddr *)&from);
+	doit((struct sockaddr *)&from);
 	return 0;
 }
-
-
-
-