#include "tcp.h" /* Function declarations. */ void outa_here(), setup_tty(); extern int childpid, newfd, telnet; extern int doutmp; extern char remotehost[]; /* * Read a stream socket one line at a time, and write each line back * to another stream socket. /* * Return when the connection is terminated. */ static int at_cr=0; /* State of telnet input */ sync_rw(sockfd, streamfd) int sockfd; /* network connection file descriptor */ int streamfd; /* pty file descriptor */ { extern int errno; int n, ttyfd=0; char line[BUFSIZ]; fd_set readfds, writefds; struct Buf_Len received, *newbuflen; /* Zero out fdset for select() */ FD_ZERO(&readfds); #ifdef OLDDEBUG char *opt_str, opt_ind[24]; #endif /* * Now do that I/O thang. :) */ for ( ; ; ) { FD_SET(sockfd, &readfds); FD_SET(streamfd, &readfds); select(10, &readfds, 0, 0, NULL); if ( FD_ISSET(sockfd, &readfds) ) { if ( (n=read(sockfd, line, BUFSIZ)) <= 0 ) { if (n == 0) exit(0); /* connection terminated */ else if (n < 0) { perror("socket read error"); exit(3); } } strncpy(received.buffer, line, n); received.len=n; if ( telnet ) newbuflen=negotiate(sockfd, &received); else newbuflen=(&received); if ( telnet ) { /* Map cr-null, cr-lf to cr */ int mapped=0, i, j; for ( i=0, j=0; i<newbuflen->len; ++i ) { #ifdef DEBUG switch (newbuflen->buffer[i]) { case '\r': fprintf(stderr, "CR"); break; case '\n': fprintf(stderr, "NL"); break; case '\0': fprintf(stderr, "NULL"); break; default: break; } #endif /* DEBUG */ if ( at_cr ) { #ifdef DEBUG fprintf(stderr, "AT_CR\r\n"); #endif at_cr=0; switch (newbuflen->buffer[i]) { case '\0': case '\n': #ifdef DEBUG fprintf(stderr, "MAP\r\n"); #endif mapped=1; continue; default: break; } } if ( newbuflen->buffer[i] == '\r' ) at_cr=1; line[j++]=newbuflen->buffer[i]; } /* Only bother re-copying if we mapped a NUL */ if ( mapped ) { strncpy(newbuflen->buffer, line, j); newbuflen->len=j; mapped=0; } } if (writen(streamfd, newbuflen->buffer, newbuflen->len) != newbuflen->len) return(-1); } if ( FD_ISSET(streamfd, &readfds) ) { n=read(streamfd, line, BUFSIZ); if ( n == 0 ) { #ifdef OLDDEBUG fprintf(stderr, "stream read returned 0.\r\n"); #endif exit(0); } else if (n < 0) { if ( errno == EIO ) continue; else { perror("pty read error"); exit(3); } } if (writen(sockfd, line, n) != n) { perror("write sockfd error"); exit(3); } } } } /* * Initialize a pty, fork a command running under it, and then * return the master file descriptor */ int setup(argv, pw) char *argv[]; struct passwd *pw; { int master_fd, slave_fd; int n, fd; if ( (master_fd=get_pty_master()) < 0 ) { perror("get_pty_master()"); exit(3); } /* If the program dies before we get to the sync-rw(), we're outahere. */ signal(SIGCLD, outa_here); if ( (childpid=fork()) < 0 ) { perror("fork error"); exit(3); } else if ( childpid == 0 ) { dropctty(); /* Lose controlling tty */ /* Gain slave as controlling tty */ if ( (slave_fd=get_pty_slave()) < 0 ) { perror("get_pty_slave()"); exit(3); } #if defined(TIOCSCTTY) && !defined(CIBAUD) if ( ioctl(slave_fd, TIOCSCTTY, NULL) < 0 ) perror("TIOCSCTTY error"); #endif /* BSD */ /* Rearrange file descriptors */ close(0); dup(slave_fd); close(1); dup(slave_fd); close(master_fd); if ( ! netdebug ) close(2), dup(slave_fd); close(slave_fd); /* Reset the terminal */ if ( tty_reset(0) < 0 ) (void) tty_sane(0); #ifdef SIGTSTP /* Prevent non-job-control programs from dying on SIGTSTP */ signal(SIGTSTP, SIG_IGN); #endif if ( login_exec(argv, pw) < 0 ) { perror("login_exec() error"); exit(255); } /* NOTREACHED */ } dropctty(); /* Lose controlling tty */ setup_tty(pw); /* Change ownership of the tty and log utmp */ return(master_fd); } /* These functions take care of the tty */ extern char tty_name[]; /* from misc.c */ extern int doutmp; /* from tcpserv.c */ char *logname=NULL; int ttyowner=0; /* The owner of the tty */ void setup_tty(pw) struct passwd *pw; { struct stat sb; time_t now; int newowner; /* Make sure the tty exists */ if ( stat(tty_name, &sb) < 0 ) return; else ttyowner=sb.st_uid; if ( doutmp ) /* Set up the user to log in utmp */ { if ( pw ) logname=pw->pw_name; else if ( (pw=(struct passwd *)getpwuid(getuid())) != NULL ) logname=pw->pw_name; else logname="nobody"; (void) time(&now); (void) utmp(tty_name, logname, remotehost, now, 0); } if ( pw ) newowner=pw->pw_uid; else if ( (pw=(struct passwd *)getpwuid(getuid())) != NULL ) newowner=pw->pw_uid; else newowner=0; /* chown to root as last resort */ (void) chmod(tty_name, 0620); (void) chown(tty_name, newowner, sb.st_gid); } /* Cleanup function */ void outa_here() { struct stat sb; time_t now; if ( childpid ) kill(childpid, SIGHUP); if ( stat(tty_name, &sb) == 0 ) { if ( doutmp && logname ) { (void) time(&now); (void) utmp(tty_name, logname, NULL, now, 1); } (void) chmod(tty_name, 0666); (void) chown(tty_name, ttyowner, sb.st_gid); } exit(0); }