/* * $Id: ioland.c,v 2.59 1999/05/10 11:46:19 tln Exp $ * * Chase Research IOLAN Daemon * =========================== * Copyright 1990 - 2000 Chase Research plc * All rights reserved * * Application interface program to modem/printer ports on iolan */ /* ** v3.4.10 ** Bug - When a modem connection is dropped on a slave psuedo-tty, the ** network connection is not dropped. ** Reason - When the modem connection is dropped, the next read on that ** device returns 0, rather than -1. The check to determine ** whether the network connection should be dropped looked for ** a return of -1 with errno set to EIO. ** Fix - The check now looks for a read return of 0, as well as a -1 ** with errno set to EIO. ** ** Bug - Starting a printer on the ioland pseudo-tty hangs. ** Reason - The pseudo-tty open was succeeding, but a subsequent open ** was blocking. The printer was attempting to open the port ** twice. ** Fix - The old mechanism of a blocking read being broken by a 2 ** second alarm was replaced with a select system call, which ** handles multiple opens using the TIOCTRAP, TIOCREQCHECK etc. ** ioctls. ** ** v3.4.11 ** Bug - When a network connection is lost, the daemon fails to signal ** the application. ** Reason - The buflen variable used to indicate a 0 return from the read ** was not being reset in the case where a read is skipped, i.e. ** the select call is broken by a SIGPIPE from the child on lost ** network connection, and was thus incorrectly indicating a ** successful read. As a side effect, the contents of the previous ** buffer was re-written to the network. ** Fix - Reset buflen to 0 when select is broken and read is skipped. */ char *rcsid = "@(#) 3.4.08 $Revision: 2.59 $"; #include #if defined(HP) || defined(HP10) # include #endif #include #include #ifdef XENIX_TYPES /* cope with u_short insanity in Xenix socket.h */ #include #endif #include #ifndef __NetBSD__ #include #endif #include #include #include #include #include #include #include #ifndef HP # ifndef BSDPTY # ifdef MIPS # include "/usr/include/sys/stropts.h" # else # include # endif # endif #endif #ifdef AUTOPUSH # ifndef HP10 # include # else # include # endif # include # include # include #endif /* AUTOPUSH */ #ifdef MIPS # include "/usr/include/sys/termio.h" #else # ifdef BSDI3 # include # else # ifdef RHLINUX5 # include # else # include # endif # endif #endif #include "chase.h" /* Following definition is correct for 386/ix TCP 1.2 */ #if defined(AIX32) || defined(AIX31) # define AIX 1 # define BSDPTY 1 #endif # ifdef HP10 # define SAD_DRIVE_DEV "/dev/sad" #else # define SAD_DRIVE_DEV "/dev/sad/admin" #endif #ifdef AIX # define LINK symlink #else # define LINK link #endif #ifdef __NetBSD__ #define MAXINT INT_MAX #endif # if !defined(ECONNREFUSED) # define ECONNREFUSED 126 # define EADDRINUSE 113 # endif #define TRUE 1 #define FALSE 0 #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif /* RHW041192 - Abort (re)connection attempts */ #define CABORT 2 #define NAMSIZE 16 #define PRNTR 0 #define TRAN 1 #define PERM 2 #define LOG_FILE "/etc/ioland.lg" #define CONF_FILE "/etc/ioland.cf" #ifdef DEFBUF # define BFSZ BUFSIZ #else # define BFSZ 4096 #endif #define DBX(n) if(dbl & n) /* DBX levels * * 0 Let the world know we're alive -- but nothing else * 1 Reports startup options * 2 Reports connections and disconnections * 4 Reports numbers of characters being transferred * 8 Shows incoming data strings as written to host * 16 Shows outgoing data strings * 32 Shows telnet negotiations * 64 Shows incoming data strings as read from iolan * 128 Shows data read from pty */ /* * Pure BSD systems may only have bzero & bcopy, if so set -DBM on the * compile line. WARNING do not use memset to set memory to values * other than zero, this defintion ignores the middle param to memset. */ #ifdef BM #define memset(a,c,b) bzero(a,b) #define memcpy(b,a,c) bcopy(a,b,c) #endif /* * External variables - needed for getopt. */ extern char *optarg; /* associated argument value */ extern int optind; /* next argv index */ /* Global Variables */ char let1[]= "pqrabcefghijklmnostuvwxyz";/* fourth letter of pseudo tty names */ char let2[]= "0123456789abcdef"; /* fifth letter of pseudo tty names */ /* RHW170393 - clone driver name was overlapping into linkname var space */ char ptyname[2*NAMSIZE]; /* master pseudo tty file name used by daemon */ char slavename[NAMSIZE]; /* slave pseudo tty name ... */ char linkname[NAMSIZE]; /* ... and the link file for it */ char argstr[1024]; /* args string to dist. daemons in log file */ #ifdef FTTY char *version = "3.4.13x"; /* Current version number */ #else char *version = "3.4.13"; /* Current version number */ #endif char obuf[BFSZ*3]; /* Buffer used for map_nl & telnet mapping */ int sock_fd; /* File descriptor for remote TCP connection.*/ int sleeptime; int pid = -1; /* return process id from a fork call */ /* REVISION - 13 OCT 1998 - TLN - Added following variables to support metered output. */ int read_size = BFSZ; int metered_read = FALSE; int master_fd; /* Input device file descriptor */ #ifdef PTY_PER_JOB # define MASTER_FD afd int afd; /* Alternate Input device file descriptor */ /* * once we've seen data on the master, we must allocate at new pty etc. */ Bool data_rxd_on_master = FALSE; #else /* * because we only need one file descriptor all the way through */ # define MASTER_FD master_fd #endif /* PTY_PER_JOB */ int slave_pid = -1; /* RHW011292 - pid of slave process */ int slave_fp = -1; /* RHW191192 - handle to slave pseudo-tty */ struct sockaddr_in sina; /* Data structure used to pass addr to kernel */ struct hostent *iolan_p; /* Ptr to structure giving remote ip address */ unsigned short remote_port = 0; /* Remote port number to connect to */ Bool connected; /* Used to determine if iolan is connection */ Bool init_pty = FALSE; /* reset pseudo-tty link */ int brk_char_seen; /* Number of break characters seen so far */ extern int errno; /* system call error number */ int IAC_seen; /* Number of IAC characters seen */ char IAC_command; /* Place to put character after IAC */ extern char *ctime(); /* system call for clock time */ /* * Globals derived from args by parseargs() */ static char conf_name[64]; /* name of configuration file */ static char called_name[64]; /* name this program was called under */ static int child_process = FALSE; /* Are we a child process ?, until cmd*/ /* line tells us otherwise, assume not*/ #ifdef HP10 static int oflags = O_RDWR | O_NONBLOCK; /* flags for opening master pseudo-tty*/ #else static int oflags = O_RDWR; /* flags for opening master pseudo-tty*/ #endif static short dev_type = TRAN; /* type of dev attached to iolan port */ static Bool nl_map = FALSE; /* Set to true for cr/nl mapping */ /* RHW010493 - new global */ static Bool hangup = FALSE; /* hangup slave if lose network */ static Bool ptyopen = FALSE; /* keep slave pty open */ static int ptyfd = -1; /* slave pty file descriptor */ static int conntout = 0; /* Num of secs to try for connection */ /* before closing the iolan conn. */ static int dbl = 0; /* Debug level */ static int alrmtout = 2; /* alarm intervals for CHKRD */ static Bool telnet_mode = FALSE; /* Set if telnet mode is enabled */ /* RHW140892 version 1.7 modification - Telnet definitions and variables */ # define DATA_STATE 0 /* TELNET states */ # define IAC_STATE 1 # define WILL_STATE 2 # define WONT_STATE 3 # define DO_STATE 4 # define DONT_STATE 5 # define CHAR1_STATE 1 /* user do break states */ # define CHAR2_STATE 2 # define BREAK_STATE 3 #define IAC 255 #define DONT 254 #define DO 253 #define WONT 252 #define WILL 251 #define AYT 246 #define IP 244 #define BREAK 243 #define DM 242 #define TELOPT_TM 6 /* * RHW220393 - Take out NULs kludge from break and timing mark sequences since * the NUL is now appended after the last carriage return. */ static unsigned char do_break[] = {IAC,BREAK}; /* TELNET perform break */ static unsigned char brkstr[NAMSIZE]; /* client perform break */ static int brklen; /* break notify length */ Bool break_mode = FALSE; /* enable Telnet breaks */ /* keepalive (-k) variables */ static unsigned char do_ayt[] = {IAC,AYT}; /* TELNET are you there */ static unsigned char do_Ayt[] = {0}; /* TELNET are you there */ Bool keepalive = FALSE; /* do our own keepalives */ Bool Keepalive = FALSE; /* do our own keepalives */ int alivetout = 30; /* interval checks in seconds */ /* RHW031292 - Telnet timemark stuff used in EOF processing. */ static unsigned char do_mark[] = {IAC,DO,TELOPT_TM};/* send timing mark */ Bool gotmark; /* received timing mark */ /* * Boolean to determine what to do with data from server. If false, write it * to the pseudo-tty else discard it. Printers may set the "-u" flag to discard * data and modems best use the non-discard default. */ Bool unidirect = FALSE; /* REVISION - 13 OCT 1998 - TLN - Added following boolean to support fixed name tty devices. */ Bool fixedtty = FALSE; /* * Boolean to determine whether to reset a pseudo-tty before or after a TCP * connection is re-established on a hangup. If the daemon is in transient mode, * it will act as if the boolean is FALSE. */ Bool waitfortcp = FALSE; /* * Booleans to push line discipline and hardware emulation modules onto the * slave pseudo-tty. Default is a raw stream. The first boolean is toggled via * the -m option. The second boolean uses the SVR4 autopush facility instead * (-a option). */ Bool pushmodules = FALSE; Bool automodules = FALSE; Bool gotalarm = FALSE; Bool gotpipe = FALSE; void do_alarm (); #ifdef HP struct request_info closeinfo; #endif /* * End of Globals derived from args by parseargs() * Note: do_break is never set by parseargs, but it makes sense to keep * it with the rest of Roland's Telnet vars. */ /* Function Prototypes */ void parseargs(); void do_banner(); void transferp(); void usage(); void channel_drained(); void sdisplay(); char *brknl_scan(); void clean_up(); void set_solinger(); void set_link(); char *errno_txt(); void set_pty(); int set_pty_once(); void use_conf_file(); void h2i_slave(); /* RHW041192 - timeout on repeated connection attempt failures */ void connabort(); /* RHW191192 - function to push terminal modules onto slave pseudo-tty */ #ifndef BSDPTY void pushmods(); #endif void prnpipe(); char *dthp_stamp(); void getargs(); void log_msg(); /* RHW140892 version 1.7 modification - Telnet functions */ char *tn_scan(); void timemark(); #ifdef AUTOPUSH void clearpush(); #endif /* * Iolan daemon entry and startup routine * ====================================== * * Process command line arguments * Set up pseudo tty line * Set up standard and log files * Spawn transfer process * Exit */ #if defined(HP) || defined (HP10) #define EF #endif main(argc,argv) int argc; char **argv; { int trapon = 1; /* ** REVISION - 13 OCT 1998 - Added the following variable. It is ** required due to the fact that the fixed tty option adds a ** parameter. This changes the position of the host ** name parameter relative to the last argument. */ int host_arg; iolan_p = NULL; getargs(argc, argv); /* init argstr for use in dbg'ing */ /* must have effective uid root to run this */ if ( geteuid() != 0 ) { fprintf(stderr,/* to tty */ "You must have an effective id of root to run %s\n", argv[0]); exit(-1); } /* if no parameters, read CONF_FILE */ if(argc == 1) { strcpy(conf_name, CONF_FILE ); strcpy(called_name,argv[0]); use_conf_file(); } parseargs(argc,argv); do_banner(); /* need at least a host name, port number and link name */ if(argc < 4) { usage(); exit(1); } /* map the name of the host into an IP address */ /* ** REVISION - 13 OCT 1998 - Determine which parameter holds host name. ** This depends on whether the fixed name tty option is used or not. */ if(fixedtty) host_arg = argc - 4; else host_arg = argc - 3; if((iolan_p = gethostbyname(argv[host_arg])) == NULL) { fprintf(stderr,/* to tty */ "error: invalid host name %s\n",argv[host_arg]); exit(1); } /* validate iolan port number */ if((remote_port = (unsigned short)atoi(argv[host_arg + 1])) == 0) { fprintf(stderr,/* to tty */ "error: non-zero port number required\n"); exit(1); } /* set the link name for the chosen pseudo-tty */ /* ** REVISION - 13 OCT 1998 - TLN - If the fixed tty option is used, ** read both a master and slave device name. Otherwise just read ** a link name. */ if(fixedtty) { sprintf(ptyname,"/dev/%s",argv[argc-2]); sprintf(slavename,"/dev/%s",argv[argc-1]); } else sprintf(linkname,"/dev/%s",argv[argc-1]); #ifdef MIPS /* disassociate daemon from controlling tty */ ioctl ( 0, TIOCNOTTY, 0 ); #endif /* close and reopen standard streams */ close(0); close(1); close(2); /* Reopen in and out to /dev/null */ (void) open("/dev/null",O_RDONLY); (void) open("/dev/null",O_WRONLY); /* standard error goes to a log file */ if(open(LOG_FILE,O_WRONLY|O_CREAT|O_APPEND,0666) == -1) exit(1); /* Make stderr fully buffered, unbuffered is the default */ #ifndef MIPS setvbuf(stderr, (char *)0, _IOFBF, 0); #endif log_msg("%s version : %s,\n\tstarting up using args %s\n", argv[0], version, argstr); /* find an appropriate pseudo-tty file */ if (set_pty_once() < 0) /* set_pty_once() will have already logged the failure */ exit(1); #if defined(HP) || defined (HP10) if (fixedtty) { if ( ioctl ( master_fd, TIOCTRAP, &trapon ) == -1 ) DBX(2) log_msg ( "cannot set tioctrap(%d)\n", errno ); /* ** Causes the process on the slave side to block until the ** daemon has cleared it. Otherwise, the open on the slave ** pty would fail. */ if ( ioctl ( master_fd, TIOCSIGMODE, TIOCSIGBLOCK ) == -1 ) DBX(2) log_msg ("cannot set tiocsigmode(%d)\n", errno); } #endif #ifdef PTY_PER_JOB /* ** promote the alternate FD to be current, it will be serviced now, ** note it hasn't rxd data yet. */ master_fd = afd; /* ** poke rubbish into afd, so that coding errors can't affect fd stream. ** afd will be assigned a meaningful value when data arrives on fd & ** set_pty() is called. */ afd = -1; #endif /* PTY_PER_JOB */ log_msg("daemon using master pseudo-tty %s\n", ptyname); DBX(1) { if(nl_map) { log_msg("CR/LF mapping switched on\n\tDebug level set to %d\n",dbl); } } /* Now fork and create the data transfer process */ if((pid = fork()) == -1) { log_msg("fork: cannot create transfer process\n"); exit(1); } /* If master, return control to calling process */ if(pid != 0) exit(0); /* Otherwise, crank up the appropriate transfer process */ setpgrp(); /* * fork again to lose slave pseudo tty as control terminal */ if ( pushmodules == TRUE ) { if ( ( pid = fork() ) == -1 ) { log_msg("fork: cannot create transfer process\n"); exit(1); } if(pid != 0) exit(0); } transferp(getpid() + 1); return(0); } /* main */ /* * RHW091192 - add timeout argument for connection attempts */ /* REVISION - 13 OCT 1998 - TLN - Added 'F' option - fixed tty name. Only allowed if fixed tty support is compiled in. */ #ifdef FTTY #define GETOPT_ARGS "CTpohnmauwFA:f:k:K:t:x:s:S::c:" #else #define GETOPT_ARGS "CTpohnmauwA:f:k:K:t:x:s:S::c:" #endif /* process command line arguments - see usage function * sets a series of globals defined near top of file, headed with a comment * "Globals derived from args by parseargs()" */ void parseargs(argc,argv) int argc; char **argv; { /* should we call use_conf_file() after argv parsed ? */ Bool call_use_conf_file = FALSE; int flag; /* currently processed arg */ while((flag = getopt(argc,argv,GETOPT_ARGS)) != EOF) { switch(flag) { case 'C': /* * Child Process - so don't read any files in, 'cause * you might loop horribly. Note this relies on getopt * parsing flags from left to right + the call to system * is set up with -C before all other args. */ child_process = TRUE; break; case 'f': strcpy(conf_name,optarg); strcpy(called_name,argv[0]); call_use_conf_file = TRUE; break; case 'u': unidirect = TRUE; /* unidirectional printing */ oflags = O_RDONLY; break; #ifdef FTTY case 'F': fixedtty = TRUE; break; #endif case 'p': dev_type = PERM; /* permanent connection */ oflags = O_RDWR; break; #ifndef BSDPTY case 'm': pushmodules = TRUE; /* manually push modules */ break; #endif #ifdef AUTOPUSH case 'a': automodules = TRUE; /* automatically push modules */ break; #endif /* * RHW200194 - The -k option allows user to instruct the daemon to check every * 30 seconds that the TCP connection is still alive. */ case 'k': keepalive = TRUE; alivetout = atol ( optarg ); if ( alivetout <= 0 || alivetout > MAXINT ) { fprintf(stderr, "Illegal keepalive timeout value.\n" ); usage(); exit(1); } DBX(2) fprintf ( stderr, "keepalive timeout = %ds\n", alivetout ); break; case 'K': Keepalive = TRUE; alivetout = atol ( optarg ); if ( alivetout <= 0 || alivetout > MAXINT ) { fprintf(stderr, "Illegal keepalive timeout value.\n" ); usage(); exit(1); } DBX(2) fprintf ( stderr, "keepalive timeout = %ds\n", alivetout ); break; /* * RHW010493 - The -h option allows user to instruct the daemon to hangup any * slave process. */ case 'h': hangup = TRUE; break; /* * Option to keep the pseudo-tty link permanently up (apart from hangups) * so as to prevent flushing of data on a slave pty close. The other option is * to set the option NOFLSH but some applications and systems may not support * or allow this. */ case 'o': ptyopen = TRUE; break; case 'w': waitfortcp = TRUE; break; /* * RHW091192 - Add timeout for connection attempts to an iolan port. Default * is 300 seconds. Flag value is interpreted in seconds. A value of 0 indicates * an infinite timeout. */ case 'c': conntout = atol ( optarg ); if ( conntout < 0 || conntout > MAXINT ) { fprintf(stderr, "Illegal connection timeout value.\n" ); usage(); exit(1); } DBX(2) fprintf ( stderr, "connection timeout = %ds\n", conntout ); break; case 'n': nl_map = TRUE; /* turn CR/NL mapping on */ break; case 'x': dbl = atoi(optarg); /* set the global debug level */ break; /* * RHW140892 version 1.7 modification - add -s flag to specify character * sequence which triggers transmission of Telnet send break request. */ case 's': /* set break notification string */ brklen = MIN(NAMSIZE-1,strlen(optarg)); strncpy(brkstr,(char *)optarg,brklen); break_mode = TRUE; DBX(1) { fprintf(stderr,/* to tty */ "Break string is %s\n", brkstr); } break; /* REVISION - 13 OCT 1998 - TLN - Added following case to set amount of data (in cps) to read per second in data metering mode. If no value is specified, data will be transferred as quickly as possible. */ case 'S': read_size = atol ( optarg ); metered_read = TRUE; DBX(1) { fprintf(stderr,/* to tty */ "set to read %d charaters per second\n", read_size); } break; case 'T': telnet_mode = TRUE; break; case 'A': alrmtout = atol ( optarg ); if ( alrmtout < 0 || alrmtout > MAXINT ) { fprintf(stderr, "Illegal alarm timeout value.\n" ); usage(); exit(1); } DBX(2) fprintf ( stderr, "alarm timeout = %ds\n", alrmtout ); break; default: usage(); /* unknown arguments */ exit(1); break; } /* end switch flags */ } /* while flags to examine */ if ( pushmodules == TRUE && automodules == TRUE ) { fprintf(stderr, "Error: the -m and -a options are mutually exclusive\n" ); usage(); exit(1); } if ( dev_type == TRAN && waitfortcp == TRUE ) { fprintf(stderr, "Error: the -w option requires the -p option\n" ); usage(); exit(1); } if ( dev_type == TRAN && ptyopen == TRUE ) { fprintf(stderr, "Error: the -o option requires the -p option\n" ); usage(); exit(1); } if ( call_use_conf_file == TRUE ) { if ( child_process == TRUE ) { fprintf(stderr,/* to tty */ "Nested config files are not allowed !\n"); fprintf(stderr,/* to tty */ " i.e. don't put the -f flag in a file.\n"); usage(); exit(1); } use_conf_file(); } } /* parseargs */ /* * Function to ID ourselves to the punters */ void do_banner() { static int header_printed = FALSE; if ( header_printed ) { /* each instance of this process will print at most one header */ return; } if (child_process) { /* child processes don't get to print a header out no matter what */ return; } header_printed = TRUE; printf("\n\t+-----------------------------------------------------------+\n"); printf("\t| |\n"); printf("\t| ioland version %-43s|\n", version); printf("\t| This software is licensed solely for use with Chase |\n"); printf("\t| Research products. All other uses are strictly prohibited |\n"); printf("\t| |\n"); printf("\t| Copyright 1990-2000 |\n"); printf("\t| Chase Research plc |\n"); printf("\t| |\n"); printf("\t+-----------------------------------------------------------+\n\n"); } /* ========================================================================= * Function to read arguments from a configuration file and use this data * to call ioland/tcpprint * ========================================================================*/ void use_conf_file() { char command_line[80]; char c[2]; Bool data_seen; int conf_fd; /* conf file FD */ /* announce our presence to the world, if we haven't already */ do_banner(); fprintf(stderr,/* to tty */ "Reading data from %s\n",conf_name); if((conf_fd = open(conf_name,O_RDONLY)) == -1) { fprintf(stderr,/* to tty */ "Cannot open %s for read\n",conf_name); exit(1); } /* Read in some command lines */ /* * RHW261092 - changed from error c[2] = ... */ c[1] = '\0'; data_seen = FALSE; strcpy(command_line,called_name); strcat(command_line," -C "); /* all system'ed procs have the -C flag to prevent recursion */ while(TRUE) { if(read(conf_fd,c,1) <= 0) { exit(0); } /* 28 Jun 2001 - TLN - Added capability to add comments to config file. */ if(c[0] == '#') { while(1) { read(conf_fd,c,1); if(c[0] == '\n') { break; } } } strcat(command_line,c); if(c[0] == '\n') { if(data_seen) { printf("Starting network daemon\n %s", command_line); #ifdef EF { char command_line2[128]; char* ptr = &command_line[strlen(command_line)]; for(;ptr >= command_line;ptr--) { if(*ptr != ' ') { break; } } for(;ptr >= command_line;ptr--) { if(*ptr == ' ') { break; } } sprintf(command_line2, "ps -ef | grep ioland | grep -v c | grep -v grep | grep %s",ptr); if(system(command_line2)) { system(command_line); } } #endif #ifdef AX { char command_line2[128]; char* ptr = &command_line[strlen(command_line)]; for(;ptr >= command_line;ptr--) { if(*ptr != ' ') { break; } } for(;ptr >= command_line;ptr--) { if(*ptr == ' ') { break; } } sprintf(command_line2, "ps -ax | grep ioland | grep -v c | grep -v grep | grep %s",ptr); if(system(command_line2)) { system(command_line); } } #endif } data_seen = FALSE; strcpy(command_line,called_name); strcat(command_line," -C "); /* all system'ed procs have the -C flag to prevent recursion */ } else data_seen = TRUE; } } /* ========================================================================= * Process to transfer data bidirectionally between a pseudotty and an iolan * * 1> Waits for data to be written to pseudotty * 2> Opens connection to iolan via socket * 3> If appropriate, starts a slave to transfer data from iolan to pseudotty * 4> Transfers data from host to iolan * 5> Continues until pseudotty and (if bidirectional) socket both 'go dry' * 6> Closes socket connection and (if bidirectional) kill the slave * 7> Repeat the process * * Note: if the connection is permanent (-p option) * * 1> Opens connection to iolan via socket * 2> Starts a slave to transfer data from iolan to pseudotty * 3> Transfers data from host to iolan * * ========================================================================*/ /* REVISION - 13 OCT 1998 - TLN - Added support for metering data output. This is required for programs which start timers when the last byte of data is read from the pseudotty (eg vsifax). Since the host's tcp/ip system can buffer a huge amount of data, the transfer will appear to occur almost immediately. */ void transferp(itoh_pid) int itoh_pid; { char rbuf[BFSZ]; /* Receive Buffer */ #ifdef HP10 int trapon = 1; int do_read; fd_set readfds; fd_set exceptfds; struct request_info rqi; #endif int buflen; /* size of transmit/receive buffer */ char c, *tbuf_p; /* working pointer to transmit buffer */ int sent; /* number of chars sent to iolan so far */ int s1; /* Number of chars sent to iolan this time */ Bool got_eof; /* EOF read from master side */ #ifdef SUN struct sigaction pipeact; #endif #ifdef CONN int sleeptot; #endif got_eof = TRUE; connected = FALSE; /* iolan is initially unconnected */ brk_char_seen = 0; /* no characters in break string seen yet */ IAC_seen = 0; /* no IAC characters seen yet */ DBX(1) log_msg("Transfer process running\n"); /* ** If this is a permanent connection, connect to the iolan immediately. */ if (dev_type == PERM) { DBX(2) log_msg("Connecting to server\n"); while (make_connection(&sleeptime) < 0) sleep(sleeptime); DBX(2) log_msg("Connected to server\n"); connected = TRUE; /* Start a slave process for iolan -> host data transfer */ if((slave_pid = fork()) == -1) { perror("fork"); exit(1); } if(slave_pid == 0) h2i_slave(itoh_pid); } /* RHW310393 - ignore any dead child processes */ #ifdef BSDI3 signal ( SIGCHLD, SIG_IGN ); #else signal ( SIGCLD, SIG_IGN ); #endif /* ** 07oct92 KVJ Call signal to ignore SIGPIPE on failed write attempt ** to network. For SCO UNIX 3.2.2 non-capture results in process ** termination. */ #ifdef SUN pipeact.sa_handler = prnpipe; pipeact.sa_mask = (sigset_t)0; pipeact.sa_flags = SV_INTERRUPT; sigaction ( SIGPIPE, &pipeact, (struct sigaction *)NULL ); #else # ifdef BSDI3 siginterrupt ( SIGPIPE, 1 ); # endif signal(SIGPIPE, prnpipe); #endif /* Allow for clean up if we get killed */ signal(SIGHUP,clean_up); signal(SIGINT,clean_up); signal(SIGQUIT,clean_up); signal(SIGTERM,clean_up); signal(SIGSEGV,clean_up); #ifndef BSDPTY if ( pushmodules == TRUE ) pushmods(); #endif while(TRUE) /* Transfer data forever */ { #ifdef CHKRD /* used by XENIX and HP/UX to detect dropped pseudo-tty */ # ifndef HP if ( dev_type == TRAN && connected == TRUE ) # endif { signal ( SIGALRM, do_alarm ); alarm ( alrmtout ); } #endif /* ** REVISION - 13 OCT 1998 - Read allocated amount of data ** (default is full buffer). */ while (TRUE) { #ifdef HP10 FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(master_fd, &readfds); FD_SET(master_fd, &exceptfds); do_read = FALSE; if (select(FD_SETSIZE, &readfds, NULL, &exceptfds, NULL) > 0) { if(FD_ISSET(master_fd,&exceptfds)) { ioctl(master_fd,TIOCREQCHECK,&rqi); if (rqi.request == TIOCCLOSE) { DBX(2) log_msg ("TIOCCLOSE\n"); ioctl(master_fd, TIOCREQSET, &rqi); buflen = 0; } else { if (rqi.request == TIOCOPEN) { DBX(2) log_msg ("TIOCOPEN\n"); } else { DBX(2) log_msg ("rqi=%d\n", rqi.request); } ioctl(master_fd, TIOCREQSET, &rqi); continue; } } if (FD_ISSET(master_fd, &readfds)) do_read = TRUE; } else { DBX(2) log_msg("select broken: errno %d\n", errno); buflen = -1; } if (do_read) #endif buflen = read(master_fd, rbuf, read_size); #ifdef HP10 else { DBX(2) log_msg ( "skipping read\n"); } #endif if (buflen > 0) break; DBX(2) log_msg ( "1: ret=%d errno=%d conn=%d alrm=%d pipe=%d\n", buflen, errno, connected, gotalarm, gotpipe ); /* ** RHW010493 - Hang up slave side if connection ** dropped by closing and reopening master. Also done ** if write to net fails. */ #ifdef HP /* ** HP/UX8 provides special ioctls to inform the master ** pseudo-tty whenever a process on the slave side ** issues an open, close or ioctl system call. The ** daemon only checks for close since this indicates ** EOF. The slave process is blocked on an open, close ** or ioctl until the daemon acknowledges receipt with ** the TIOCREQSET ioctl. This section of code is run ** every alrmtout seconds to clear these blocks and ** also to look for the close after a data transfer. ** This alrmtout second loop may incur a long delay in ** processing the slave open, an ioctl dependent ** command such as "stty", and detecting the close. A ** better implementation would be to use the "select" ** system call which is non-trivial. ** Monitoring is enabled by the TIOCTRAP ioctl called ** further back. */ if (connected == FALSE && gotpipe == TRUE && hangup == TRUE ) { gotpipe = FALSE; goto ptyreset; } if (ioctl(master_fd, TIOCREQCHECK, &closeinfo) >= 0) { if (closeinfo.request == TIOCCLOSE) { ioctl(master_fd, TIOCREQSET, &closeinfo); buflen = 0; } else ioctl(master_fd, TIOCREQSET, &closeinfo); } #endif #ifdef MOT4 /* ** Motorola special for permanent mode gettys. Reset ** the pseudo-tty if the slave side closes. */ if (buflen == -1 && errno == EINVAL && dev_type == PERM && hangup == TRUE ) goto ptyreset; #endif #ifdef HP if (buflen == -1 && gotalarm == FALSE && connected == FALSE ) #else if (buflen == -1 && errno == EINTR && connected == FALSE ) #endif { if ( hangup == TRUE ) { ptyreset: /* ** Some systems may signal the daemon ** with a hangup when the slave/master ** link drops, so ignore until finished ** resetting. */ DBX(2) log_msg ( "hanging up ...\n" ); signal(SIGHUP, SIG_IGN); if (pushmodules == TRUE && dev_type == PERM ) { close ( slave_fp ); slave_fp = -1; } close ( master_fd ); master_fd = -1; #ifdef AUTOPUSH clearpush(); #endif if (dev_type == TRAN || waitfortcp == FALSE ) { DBX(2) log_msg ( "Resetting pseudo-tty link\n" ); set_pty(); channel_drained(); } else init_pty = TRUE; signal(SIGHUP, clean_up); } else /* abort reads and reconnect to host */ break; } if ( connected == FALSE && dev_type == PERM ) break; /* ** STREAMS (& possibly other) pseudo-ttys can detect ** EOF via a zero read. */ if (got_eof == FALSE && dev_type == TRAN) { /* only disconnect once */ if ((buflen == 0) || (buflen == -1 && errno == EIO)) { DBX(2) log_msg ( "detected EOF (buflen=%d)\n", buflen ); #ifdef PTY_PER_JOB if ( data_rxd_on_master == FALSE ) set_pty(); #endif channel_drained(); #ifndef PTY_PER_JOB got_eof = TRUE; #endif } } #ifdef PTY_PER_JOB if (got_eof == FALSE && dev_type == PERM) { if (( buflen == 0 ) || ( buflen == -1 && errno == EIO )) { DBX(2) log_msg ( "detected -p EOF (buflen=%d)\n", buflen ); signal(SIGHUP, SIG_IGN); close(master_fd); #ifdef AUTOPUSH clearpush(); #endif if ( data_rxd_on_master == FALSE ) set_pty(); master_fd = afd; #ifndef BSDPTY if (pushmodules == TRUE && slave_fp == -1) pushmods(); #endif data_rxd_on_master = FALSE; afd = -1; signal(SIGHUP, clean_up); } } #endif if (got_eof == FALSE && dev_type == PERM && hangup == TRUE) { if ((buflen == 0) || (buflen == -1 && errno == EIO)) { DBX(2) log_msg ( "resetting tcp connection\n", buflen ); channel_drained(); got_eof = TRUE; break; } } #ifdef MOT4 if ( buflen == -1 ) #else #if defined( HP ) || defined( HP10 ) sleep(1); #else sleep(4); #endif #endif gotalarm = FALSE; #ifdef CHKRD /* used by XENIX and HP/UX to detect dropped pseudo-tty */ # ifndef HP if (dev_type == TRAN && connected == TRUE) # endif { // BOOGERS - Tried removing these lines signal(SIGALRM, do_alarm); alarm(alrmtout); } #endif } DBX(4) log_msg("read %d bytes from host\n", buflen); DBX(128) sdisplay("Read \"", rbuf, "\" from host", buflen); #ifdef PTY_PER_JOB if (buflen > 0 && data_rxd_on_master == FALSE) { data_rxd_on_master = TRUE; set_pty(); } #endif /* PTY_PER_JOB */ if (buflen > 0) got_eof = FALSE; /* ** Now that we're transferring data we've already read, reset ** the alarm */ gotalarm = FALSE; alarm(0); /* ** RHW191192 - close slave so that EOF can be detected from ** client. */ # ifdef PTY_PER_JOB if (pushmodules == TRUE && buflen > 0 && slave_fp != -1) # else if (pushmodules == TRUE && dev_type == TRAN && slave_fp != -1) # endif { close ( slave_fp ); slave_fp = -1; } /* ** At this point, we may be connected to the iolan. If not, ** make connection */ if(connected == FALSE) { DBX(2) log_msg("Connecting to server\n"); if ( dev_type == TRAN ) { #ifdef CONN /* ** Use a simple counter scheme for SunOS since ** a sleep will clear all alarms on completion. ** This is not as accurate as the alarm scheme. */ sleeptot = 0; #else # ifdef BSDI3 siginterrupt(SIGALRM, 1); # endif signal(SIGALRM, connabort); alarm(conntout); #endif } while (make_connection(&sleeptime) < 0) { if ( connected == CABORT ) { DBX(2) log_msg ( "Aborting connection attempts\n" ); break; } #ifdef CONN if (dev_type == TRAN && conntout > 0) if ((sleeptot += sleeptime) >= conntout) connected = CABORT; #endif sleep(sleeptime); } if ( connected == CABORT ) { connected = FALSE; continue; } else /* Got through, disable client test. */ alarm(0); DBX(2) log_msg("Connected to server\n"); if (init_pty == TRUE) { #ifdef PTY_PER_JOB if ( afd == -1 ) #endif set_pty(); channel_drained(); init_pty = FALSE; } connected = TRUE; /* ** start a slave process for iolan -> host data ** transfer. */ if ((slave_pid = fork()) == -1) { perror("fork"); exit(1); } if(slave_pid == 0) h2i_slave(itoh_pid); /* ** RHW111192 - go back to read loop if this is just a ** reconnection. */ if (buflen <= 0) continue; } /* Scan for breaks and NL -> CRLF */ tbuf_p = brknl_scan(rbuf, &buflen); DBX(16) sdisplay( "Writing \"", tbuf_p, "\" to server", buflen); /* Now, write the data to the iolan -- this may take retries */ sent = 0; /* RHW021192 - Move sent update further down code */ while ((s1 = write(sock_fd,tbuf_p + sent,buflen - sent)) != buflen) { DBX(2) log_msg ( "2: ret=%d errno=%d conn=%d buf=%d sent=%d\n", s1, errno, connected, buflen, sent ); /* ** RHW010493 - Hang up slave side if connection dropped ** by closing and reopening master. Also done if read ** from master fails. */ if (s1 == -1 && connected == FALSE && hangup == TRUE) { DBX(2) log_msg ( "hanging up ...\n" ); signal(SIGHUP, SIG_IGN); if (pushmodules == TRUE && dev_type == PERM) { close ( slave_fp ); slave_fp = -1; } close ( master_fd ); master_fd = -1; #ifdef AUTOPUSH clearpush(); #endif if (dev_type == TRAN || waitfortcp == FALSE) { DBX(2) log_msg ( "Resetting pseudo-tty link\n" ); set_pty(); channel_drained(); } else init_pty = TRUE; signal(SIGHUP, clean_up); break; } if (s1 <= 0) { DBX(2) log_msg("Connection to server lost\n"); close ( sock_fd ); /* ** RHW011292 - This was set to the value of pid. ** If the slave pseudo-tty was opened and closed ** without a data transfer (e.g. stty) then the ** master would attempt to kill the non-existant ** slave process. Unfortunately, pid was set to ** its own id so it killed itself. This is now ** fixed. */ if ( slave_pid != -1 ) { DBX(2) log_msg("Killing slave\n"); #ifdef MOT3 /* Kill slave */ kill(slave_pid,SIGKILL); #endif /* Kill the slave */ kill(slave_pid,SIGKILL); wait((int *)0); slave_pid = -1; } if ( dev_type == TRAN ) { #ifdef CONN sleeptot = 0; #else # ifdef BSDI3 siginterrupt(SIGALRM, 1); # endif signal(SIGALRM, connabort); alarm(conntout); #endif } while (make_connection(&sleeptime) < 0) { if ( connected == CABORT ) { DBX(2) log_msg ( "Aborting connection attempts\n" ); break; } #ifdef CONN if (dev_type == TRAN && conntout > 0) if ((sleeptot += sleeptime) >= conntout) connected = CABORT; #endif sleep(sleeptime); DBX(2) log_msg("Trying to reconnect to server\n"); } if ( connected == CABORT ) { connected = FALSE; break; } else /* Got through, disable client test. */ alarm(0); DBX(2) log_msg("Reconnected to server\n"); if ( init_pty == TRUE ) { #ifdef PTY_PER_JOB if (afd == -1) #endif set_pty(); channel_drained(); init_pty = FALSE; } /* ** RHW021192 - Reset connection boolean. Move ** into common code eventually. */ connected = TRUE; if ((slave_pid = fork()) == -1) { perror("fork"); exit(1); } if (slave_pid == 0) h2i_slave(itoh_pid); } else { /* ** RHW021192 - Update bytes sent only if write ** did something. Previous code was adding -1 ** to running total. */ if ((sent += s1) == buflen) break; } } DBX(4) log_msg("Wrote %d bytes to server\n", buflen); /* ** REVISION - 13 OCT 1998 - Wait a second before next read if ** metering data. */ if (metered_read) sleep(1); } } /*===============================================* * Slave process to handle iolan to host traffic * *===============================================*/ void h2i_slave(itoh_pid) int itoh_pid; { char rbuf[BFSZ]; /* Receive Buffer */ int buflen; /* size of transmit/receive buffer */ char c, *tbuf_p; /* working pointer to transmit buffer */ int s1, rc, sent; #ifdef SUN struct sigaction alrmact; #endif #ifdef PTY_PER_JOB if ( pushmodules == TRUE ) close ( slave_fp ); #endif while(TRUE) /* Transfer data forever */ { if ( keepalive == TRUE ) { #ifdef SUN alrmact.sa_handler = do_alarm; alrmact.sa_mask = (sigset_t)0; alrmact.sa_flags = SV_INTERRUPT; sigaction ( SIGALRM, &alrmact, (struct sigaction *)NULL ); #else # ifdef BSDI3 siginterrupt ( SIGALRM, 1 ); # endif signal ( SIGALRM, do_alarm ); #endif alarm( alivetout ); } if ( Keepalive == TRUE ) { #ifdef SUN alrmact.sa_handler = do_alarm; alrmact.sa_mask = (sigset_t)0; alrmact.sa_flags = SV_INTERRUPT; sigaction ( SIGALRM, &alrmact, (struct sigaction *)NULL ); #else # ifdef BSDI3 siginterrupt ( SIGALRM, 1 ); # endif signal ( SIGALRM, do_alarm ); #endif alarm( alivetout ); } /* Reads of <= 0 indicate that the iolan has closed connection */ if((buflen = read(sock_fd,rbuf,BFSZ)) <= 0) { /* * Keepalive test. Send a Telnet Are You There which is swallowed by the other * end. If write fails, assume the connection is down and reconnect. */ if ( keepalive == TRUE && gotalarm == TRUE ) { gotalarm = FALSE; if ( write ( sock_fd, do_ayt, 2 ) == 2 ) { DBX(32) log_msg ( "connection alive\n\r" ); continue; } } if ( Keepalive == TRUE && gotalarm == TRUE ) { gotalarm = FALSE; if ( write ( sock_fd, do_Ayt, 1 ) == 1 ) { DBX(32) log_msg ( "connection alive\n\r" ); continue; } } DBX(2)log_msg("Connection to server lost\n\tSlave process dying\n"); /* * RHW271092 - If slave detects lost connection before master then it should * die and send a SIGPIPE to mimic a hangup on the stream. When the master * write returns with -1 it will pick up the failure and attempt to reconnect. */ kill ( getppid(), SIGPIPE ); exit(1); } if ( keepalive == TRUE ) alarm(0); if ( Keepalive == TRUE ) alarm(0); DBX(4) log_msg("Read %d bytes from server\n", buflen); DBX(64) sdisplay( "Read \"", rbuf, "\" from server", buflen); tbuf_p = rbuf; if(telnet_mode) { tbuf_p = tn_scan(rbuf,&buflen); /* Strip telnet data out */ } if(buflen <= 0) { continue; } if ( unidirect == TRUE ) { DBX(32)log_msg("Discarding %d bytes from server\n", buflen); continue; } sent = 0; /* write the block to the host -- this may take retries */ DBX(8) sdisplay( "Writing \"", tbuf_p, "\" to host", buflen); while((s1 = write(master_fd,tbuf_p + sent,buflen - sent))!= buflen) { if ( s1 == -1 ) { DBX(2) log_msg("Write to host failed(%d)\n", errno ); break; } else if ( ( sent += s1 ) == buflen ) break; } DBX(4) log_msg("Wrote %d bytes to host\n",buflen); } } /*=======================================* * Function to handle channels going dry * *=======================================*/ void channel_drained() { alarm(0); /* Turn off alarm until reconnection */ /* * Ensure pty-per-job pseudo-ttys are changed before waiting for output to * drain as another print job may come in and use the old pseudo-tty while * we are waiting for the timing mark. */ #ifdef PTY_PER_JOB signal ( SIGHUP, SIG_IGN ); /* close the file descriptor associated with the current input stream */ close(master_fd); /* promote the alternate FD to be current, it will be serviced now, note it * hasn't rxd data yet */ master_fd = afd; data_rxd_on_master = FALSE; /* poke rubbish into afd, so that coding errors can't affect fd stream. * afd will be assigned a meaningful value when data arrives on fd & * set_pty() is called */ afd = -1; signal ( SIGHUP, clean_up ); #endif /* PTY_PER_JOB */ #ifndef BSDPTY /* * RHW141292 - Only repush modules if slave pseudo-tty was closed. */ if ( pushmodules == TRUE && slave_fp == -1 ) pushmods(); #endif if ( connected == TRUE ) { /*DBX(2) log_msg("Closing socket\n");*/ if ( telnet_mode == FALSE ) { set_solinger(); } else if ( slave_pid != -1 ) /* no slave - no data */ { /* * RHW031292 - Use the more efficent timemark Telnet option to detect when * the last data block has been read by the iolan. */ DBX(32) log_msg ( "waiting for timing mark\n" ); gotmark = FALSE; #ifdef SOL24 if ( signal ( SIGUSR2, timemark ) == -1 ) #else if ( signal ( SIGUSR2, timemark ) == SIG_ERR ) #endif { DBX(32) log_msg ( "signal failed(%d)\n", errno ); gotmark = TRUE; } /* RHW081292 - close immediately if connection is inaccessible. */ if ( write ( sock_fd, do_mark, 3 ) == -1 ) { DBX(32) log_msg ( "timing mark failed\n" ); } else { while ( gotmark == FALSE ) sleep ( 1 ); DBX(32) log_msg ( "got timing mark\n" ); } } close(sock_fd); /* Close the channel down */ } /* if connected */ if ( slave_pid != -1 ) { DBX(2) log_msg("Killing slave\n"); #ifdef MOT3 kill(slave_pid,SIGKILL); /* Kill the slave */ #endif kill(slave_pid,SIGKILL); /* Kill the slave */ wait((int *)0); slave_pid = -1; } connected = FALSE; } /* * number of spaces for one char printed as hex + spacing */ #define HEX_FIELD_SZ 5 /* * approx max number of chars we'll put on one line */ #define MAX_LINE_LEN 200 /* * output buffer watershed - don't fill beyond a sensible limit * (the -40 is a feelgood/fiddle factor.) */ #define OP_BUF_WS (sizeof(op_buf) - HEX_FIELD_SZ - MAX_LINE_LEN - 40) #define CONTINUATION_STRING " ..... " /*==========================================================*/ /* Function to display printing and non printing characters */ /*==========================================================*/ /* * Prints out the standard datestamp, * following by the init strings, the data (with most non-printable chars * expressed as hex) and finally the closing comment (so you can tell * all data was sent). Newlines are inserted in the output, after every * real newline (which will be expressed as \n) and after every 200 * chars or so on the output line, so lines don't get too long to vi. */ /* ** Args: ** init - Initial comment on the following data ** tbuf_p - The data to be displayed in printable form ** close_com - Closing comment following the data ** amount - amount of data in tbuf_p */ void sdisplay(init, tbuf_p, close_com, amount) char *init; char *tbuf_p; char *close_com; int amount; { int current_pos; /* current pos. in the input buffer */ unsigned char c; /* current char in the input buffer */ char op_buf[2*BFSZ]; /* output buffer */ char *op_ptr; /* ptr into output buffer */ char *line_start; /* ptr to start of current output line*/ Bool prev_ch_print; /* was previous char printable ? */ Bool first_pass; /* so we know when to print header */ prev_ch_print = TRUE; /* assume it was */ first_pass = TRUE; strcpy(op_buf, dthp_stamp()); /* datestamp, proc ID etc. */ op_ptr = op_buf + strlen(op_buf); line_start = op_ptr; /* * While there's more data to output */ for(current_pos = 0 ; current_pos < amount ; current_pos++) { if (first_pass == TRUE) { strcpy(op_ptr, init); /* initial comment on the data*/ op_ptr += strlen(init); first_pass = FALSE; } /* print the char, * (a) as is * (b) with leading space (prev char was HEX) * (c) in HEX */ if(((c = *(tbuf_p + current_pos)) >= 0x20 && c <= 0x7e) || c == '\t') { if ( prev_ch_print == TRUE ) { sprintf(op_ptr,"%c",c); op_ptr++; } else { sprintf(op_ptr," %c",c); op_ptr += 2; } prev_ch_print = TRUE; } else if (c == '\r') { sprintf(op_ptr,"\\r"); op_ptr += 2; prev_ch_print = FALSE; } else if (c == '\n') { sprintf(op_ptr,"\\n"); op_ptr += 2; prev_ch_print = FALSE; } else if (c == '\0') { sprintf(op_ptr,"\\0"); op_ptr += 2; prev_ch_print = FALSE; } else { /* Each hex digit printed should take 5 spaces, */ /* a leading space then "0x" plus 2 digits. */ sprintf(op_ptr," %#4.2x",c); op_ptr += HEX_FIELD_SZ; prev_ch_print = FALSE; } /* * if we've exhausted the data */ if (current_pos >= (amount - 1)) { strcpy(op_ptr, close_com); /* add closing comment to data*/ op_ptr += strlen(close_com); write(2,op_buf,strlen(op_buf)); break; /* get out of for loop */ } /* * if we've filled up the output buffer */ if (op_ptr - op_buf > OP_BUF_WS) { strcpy(op_ptr, CONTINUATION_STRING); op_ptr += strlen(CONTINUATION_STRING); write(2,op_buf,strlen(op_buf)); /* restart buffer with header */ strcpy(op_buf, dthp_stamp()); /* datestamp, proc ID etc. */ op_ptr = op_buf + strlen(op_buf); /* * We've filled the OP buffer once & are having another bash. * The previous buffer will have trailed off with a * continuation string, we'll start with one. */ strcpy(op_ptr, CONTINUATION_STRING); op_ptr += strlen(CONTINUATION_STRING); continue; } /* * if we've exceeded max line length or this char was a newline */ if ((op_ptr - line_start > MAX_LINE_LEN) || (c == '\n')) { *op_ptr = '\n'; /* Add a newline */ op_ptr++; line_start = op_ptr; prev_ch_print = TRUE; /* started newline, so pretend yes */ } } } /* * Establish a connection to the iolan routine * =========================================== * * get a tcp socket file descriptor * set up the addresses * connect to the iolan * abort if too many connection attempts failed * */ int make_connection(sleeptime) int *sleeptime; { static int fcount = 0; /* Failure counter */ static int log_fail_no = 32; /* log the Nth failure */ int on = 1; /* create a socket file descriptor connected to TCP */ if((sock_fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); /* Bail out if we can't open the socket */ /* RHW121192 - was an exit */ *sleeptime = 5; return -1; } /* * RHW031192 - The option KEEPALIVE sends a SIGPIPE signal to the mater process * if the stream goes down. */ #ifdef SOL24 if (setsockopt(sock_fd,SOL_SOCKET,SO_KEEPALIVE,(const char *)&on,sizeof(on)) < 0 ) #else if (setsockopt(sock_fd,SOL_SOCKET,SO_KEEPALIVE,&on,sizeof(on)) < 0 ) #endif { DBX(1) log_msg("Failed to setsockopt SO_KEEPALIVE(%d)\n", errno); } /* Tell the socket library that we are half the connection -- Let it pick an arbitary port number */ memset((char *)&sina, 0, sizeof (sina)); sina.sin_family = AF_INET; #ifdef ADDR if(bind(sock_fd,(struct sockaddr *)&sina,sizeof(sina)) < 0) #else if(bind(sock_fd,&sina,sizeof(sina)) < 0) #endif { perror("bind"); /* * RHW121192 - Used to return only on errno EADDRINUSE. */ /* * RHW121192 - Continual reconnection attempts will eventually drain socket * resources if the socket discarded here is not closed. */ *sleeptime = 5; close ( sock_fd ); return -1; } /* Set up the addresses */ sina.sin_port = htons(remote_port); memcpy((caddr_t)&sina.sin_addr, iolan_p->h_addr, iolan_p->h_length); /* make the connection */ #ifdef ADDR if(connect(sock_fd,(struct sockaddr *)&sina,sizeof sina) >= 0) #else if(connect(sock_fd,&sina,sizeof sina) >= 0) #endif { alarm(0); fcount = 0; /* we've succeded in connecting => 0 failures */ log_fail_no = 32; /* next time we'll log the 32nd, 64th, etc. */ return 0; } fcount++; DBX(2) { if (fcount <= 20) { log_msg("cannot connect: count = %d, errno = %d%s\n", fcount, errno, errno_txt(errno)); } else if (fcount == log_fail_no) { log_msg("failed to connect %d times, last errno = %d%s\n", fcount, errno, errno_txt(errno)); log_fail_no <<= 1; if (log_fail_no < 0) { log_fail_no = 32; /* in case the number wraps ! */ } } } if ( fcount > 20 ) *sleeptime = 10; else *sleeptime = 1; close(sock_fd); return -1; } /* make_connection */ char * errno_txt(err) int err; { switch(err) { case 102: /* Operation already in progress */ return("(probably EALREADY)"); case 112: /* Address family not supported by protocol family */ return("(probably EAFNOSUPPORT)"); case 113: /* Address already in use */ return("(probably EADDRINUSE)"); case 114: /* Can't assign requested address */ return("(probably EADDRNOTAVAIL)"); case 116: /* Network is unreachable */ return("(probably ENETUNREACH)"); case 121: /* Socket is already connected */ return("(probably EISCONN)"); case 125: /* Connection timed out */ return("(probably ETIMEDOUT)"); case 126: /* Connection refused */ return("(probably ECONNREFUSED)"); default: return(""); } } /* errno_txt */ void set_pty() { static int pty_fc = 0; /* PTY Failure counter */ static int pty_lfn = 32; /* log the Nth failure */ int trapon = 1; while (set_pty_once() == -1) { signal(SIGHUP, SIG_IGN); close(MASTER_FD); set_link(FALSE); /* ** all this gibberish is to prevent the log from filling up! ** it seemed stupid to summarize for connection attempts, then ** fill it with a load of set_pty failure messages in the log. */ DBX(2) { pty_fc++; if (pty_fc <= 20) { log_msg("cannot allocate any ptys: count=%d\n", pty_fc); } else if (pty_fc == pty_lfn) { log_msg("failed to allocate any pty %d times\n", pty_fc); pty_lfn <<= 1; if (pty_lfn < 0) { /* in case the number wraps ! */ pty_lfn = 32; } } } signal(SIGHUP, clean_up); sleep(5); } pty_fc = 0; /* we've succeded in allocating a pty => 0 failures */ pty_lfn = 32; /* next time we'll log the 32nd, 64th, etc. */ #if defined(HP) || defined(HP10) if (fixedtty) { /* ** enable trapping of slave pseudo-tty close requests to get ** EOF. */ if (ioctl(master_fd, TIOCTRAP, &trapon) == -1) DBX(2) log_msg ( "cannot set tioctrap(%d)\n", errno ); /* ** Causes the process on the slave side to block until the ** daemon has cleared it. Otherwise, the open on the slave pty ** would fail. */ if ( ioctl ( master_fd, TIOCSIGMODE, TIOCSIGBLOCK ) == -1 ) DBX(2) log_msg ("cannot set tiocsigmode(%d)\n", errno); } #endif } /* * Set up the pseudo-tty links routine * =================================== * * find an unused master/slave pseudo-tty pair * set up ownership and permissions for tty pair * inform user of choice for configuration purposes * */ int set_pty_once ( /* uses global oflags - open flags for master pseudo-tty */ ) { /* REVISION - 13 OCT 1998 - TLN - Added support for fixed name tty devices. NOTE: This support is available if compiled in. */ if(!fixedtty) { #ifdef CLONE extern char *ptsname(); /* declared 'cause some systems don't */ /* RHW160192 version 1.5 modification - UNIX System V Release 4 uses a different * scheme for allocating master and slave pseudo ttys pairs. */ # ifdef AUTOPUSH struct strapush modules; /* modules to push on slave stream */ struct stat ttybuf; /* file info on slave pseudo-tty */ int sac_fd; /* STREAMS admin node file dsecriptor */ # endif /* AUTOPUSH */ /* use clone driver to obtain a master pseudo-tty */ # ifdef AIX32 # define CLONE_DRIVE_DEV "/dev/ptc" # else # ifdef HP # define CLONE_DRIVE_DEV "/dev/ptym/clone" # else # define CLONE_DRIVE_DEV "/dev/ptmx" # endif # endif if((MASTER_FD = open(CLONE_DRIVE_DEV, oflags)) == -1) { log_msg("cannot obtain master tty\n"); return(-1); } # ifndef AIX32 # ifndef HP /* unlock slave pseudo-tty for access */ if(unlockpt(MASTER_FD) == -1) { log_msg("cannot unlock pty(%d)\n",errno); return(-1); } # endif # endif /* init the master pseudo-tty device pathname, for debugging purposes */ if(!fixedtty) { sprintf(ptyname, "%s %s", CLONE_DRIVE_DEV, "(clone driver)"); /* obtain the slave pseudo-tty device pathname */ # ifndef AIX32 strcpy(slavename,ptsname(MASTER_FD)); # else strcpy(slavename,ttyname(MASTER_FD)); # endif if(slavename[0] == '\0') { log_msg("cannot obtain slave pty name(%d)\n",errno); return(-1); } DBX(2) log_msg ( "slave is %s\n", slavename ); set_link(TRUE); } # ifdef AUTOPUSH /* Now access streams administration (SAD) driver to configure modules to push * onto slave pseudo-tty when it is opened by any process. */ if ( automodules == TRUE ) { if ( ( sac_fd = open ( SAD_DRIVE_DEV, O_RDWR ) ) == -1 ) { log_msg("cannot obtain sad driver %s(%d)\n",SAD_DRIVE_DEV,errno); return(-1); } /* Auto-push line discipline module (ldterm) and pseudo-tty device driver * (ptem) for the slave pseudo-tty. */ if ( stat ( slavename, &ttybuf ) == -1 ) { log_msg("stat of slave %s failed\n",slavename); close ( sac_fd ); return(-1); } /* RHW160192 version 1.5 modification - If the daemon was restarted without a * reboot then the module configuration will still exist. Thus clear any stack * configuration before setting up new one. */ #ifdef HP10 modules.sap_cmd = SAP_CLEAR; modules.sap_major = major(ttybuf.st_rdev); modules.sap_minor = minor(ttybuf.st_rdev); #else modules.sap_common.apc_cmd = SAP_CLEAR; modules.sap_common.apc_major = major(ttybuf.st_rdev); modules.sap_common.apc_minor = minor(ttybuf.st_rdev); #endif ioctl(sac_fd,SAD_SAP,&modules); #ifdef HP10 modules.sap_cmd = SAP_ONE; modules.sap_npush = 2; #else modules.sap_common.apc_cmd = SAP_ONE; modules.sap_common.apc_npush = 2; #endif strcpy(modules.sap_list[0],"ptem"); strcpy(modules.sap_list[1],"ldterm"); if ( ( ioctl ( sac_fd, SAD_SAP, &modules ) ) == -1 ) { log_msg("cannot configure slave modules(%d)\n",errno); close ( sac_fd ); return(-1); } close ( sac_fd ); } /* automodules */ openslave(); return(0); # endif /* AUTOPUSH */ #else /* !CLONE */ # ifdef SEQUENT /* RHW271092 - rewrite this sequent section */ char *mptr; char *sptr; /* get name of and open master tty */ if ( ( MASTER_FD = getpseudotty(&sptr, &mptr) ) == -1 ) { log_msg("no pseudo tty available\n"); return ( -1 ); } strcpy( slavename, sptr ); strcpy( ptyname, mptr ); /* * revoke all access to the slave pseudo-tty and stop it being a controlling tty */ fvhangup( slavename ); DBX(2) log_msg ( "daemon using slave %s\n", slavename ); chown( ptyname, getuid(), getgid() ); chmod( ptyname, 0666 ); set_link(TRUE); openslave(); return(0); # else /* SEQUENT */ # ifdef SGI extern char *_getpty(int *, int, mode_t, int); char *s; if((s = _getpty(&MASTER_FD, oflags|O_NDELAY, 0660, 0)) == NULL){ fprintf(stderr, "Cannont open pseudo-tty device.\n"); return (-1); } /* Get the slave tty name. */ strcpy(slavename, s); set_link(TRUE); openslave(); return(0); # else /* XENIX and the rest */ Bool got_tty; /* boolean to flag procurement of pseudo-tty */ Bool first_attempt; /* boolean to flag timestamp of 1st msg */ int c1,c2; /* iteration counters */ char buffer[160]; struct stat perms; got_tty = FALSE; first_attempt = TRUE; /* Only timestamp pty not available msgs once */ /* now find an unused pseudo tty device file */ for(c1=0; let1[c1] != '\0' ;c1++) { if(got_tty) { openslave(); return(0); } for(c2=0; let2[c2] != '\0' ;c2++) { /* sequentially try all pseudo-ttys from pty[a-z][0-99a-f] for masters */ # ifdef HP # define MASTER_PTY_FMT "/dev/ptym/pty%c%c" # define SLAVE_PTY_FMT "/dev/pty/tty%c%c" # else # define MASTER_PTY_FMT "/dev/ptym/pty%c%c" # define SLAVE_PTY_FMT "/dev/pty/tty%c%c" # endif /* HP */ sprintf(ptyname, MASTER_PTY_FMT, let1[c1],let2[c2]); sprintf(slavename, SLAVE_PTY_FMT, let1[c1],let2[c2]); if((MASTER_FD = open(ptyname,oflags)) >= 0) { /* tty is unused - set up ownership and modes */ chown(ptyname,getuid(),getgid()); chmod(ptyname,0666); /* the slave (re)links up with the user defined name */ set_link(TRUE); got_tty = TRUE; DBX(2) log_msg("%s is available\n", ptyname); break; } /* if found a tty */ DBX(2) { if (first_attempt == TRUE) { log_msg("%s not available\n", ptyname); first_attempt = FALSE; } else { /* bodge to do away with passing datestamp each time */ /* log_msg("\t not available\n", ptyname); */ sprintf(buffer,"\t%s not available\n", ptyname); write(2,buffer,strlen(buffer)); } } } } /* all pseudo-ttys were in use - exit and try again later */ log_msg("There are no pseudo-tty devices available.\n\ \tPlease contact your system administrator.\n"); return(-1); # endif /* SEQUENT */ # endif /* SGI */ #endif /* CLONE */ return 0; } #ifdef FTTY else /* using fixed tty */ { /* First attempt to open the slave device. If this fails, the program will exit without returning. */ openslave(); /* Now, open the master device and take ownership of it. */ if((MASTER_FD = open(ptyname,oflags)) >= 0) { chown(ptyname,getuid(),getgid()); chmod(ptyname,0666); return 0; } /* if found a tty */ perror(ptyname); return(-1); } #endif } /* set_pty_once */ /* * #defs for usage text. */ #define PROGNAME "ioland " /* all this forms the first line ! */ #define USG "usage: " #define USG_ARGS "[options] server port link\n\nwhere:\n" #define USG_FARGS "-F [options] server port master slave\n\nwhere:\n" #define USG_SERVER " server is the name of the target server.\n" #define USG_PORT " port is the TCP port on the target server.\n" #define USG_LINK " link is the user's name for the pty device.\n" #define USG_MASTER " master is the name of the master pty device.\n" #define USG_SLAVE " slave is the name of the slave pty device.\n" #define USG_OPTS "\nand the available options are:\n" /* a line of explanation for each flag */ #define USG_F_FLG " -f \"file\" Read configuration parameters from file \"file\".\n" #define USG_N_FLG " -n Map \\n's into \\r\\n pairs.\n" #define USG_X_FLG " -x Set debugging level to .\n" #define USG_P_FLG " -p Establish permanent connection to port on server.\n" #define USG_S_FLG " -s \"str\" Set break notification string to \"str\".\n" #define USG_TT_FLG " -T Set telnet mode.\n" #define USG_O_FLG " -o Set pty open mode.\n" #define USG_H_FLG " -h Send hangup to application if network connection is lost.\n" #define USG_C_FLG " -c Try to connect to server for seconds. \ If -c isn't set, ioland\n\ tries to connect for 300 secs, -c 0 causes infinite retries.\n" #define USG_K_FLG " -k Check connection is up every seconds.\n" #define USG_IK_FLG " -K Invisably check connection is up every seconds.\n" #define USG_U_FLG " -u Ignore data from server.\n" #define USG_W_FLG " -w reset tcp before pty on a hangup.\n" #ifndef BSDPTY # define USG_M_FLG " -m Push tty modules onto stream.\n" #endif #ifdef AUTOPUSH #define USG_A_FLG " -a Push modules using autopush.\n" #endif #define USG_CF_FILE "\n If %sis run with no parameters, data is read from %s\n\n" /* ====================================================================== * * Command usage routine * * print a few lines explaining to the user how to invoke the program * * ======================================================================*/ void usage() { /* usage comments will help when looking at grep output (honest) */ /* * Put out usage: line in pieces, so we can ifdef it for all flavours */ fprintf(stderr, /* usage */ USG); fprintf(stderr, /* usage */ PROGNAME); fprintf(stderr, /* usage */ USG_ARGS); fprintf(stderr, /* usage */ USG_SERVER); fprintf(stderr, /* usage */ USG_PORT); fprintf(stderr, /* usage */ USG_LINK); /* REVISION - 13 OCT 1998 - TLN - Added support for fixed tty names. */ #ifdef FTTY fprintf(stderr, /* usage */ "\n\t\tOR\n\n"); fprintf(stderr, /* usage */ USG); fprintf(stderr, /* usage */ PROGNAME); fprintf(stderr, /* usage */ USG_FARGS); fprintf(stderr, /* usage */ USG_SERVER); fprintf(stderr, /* usage */ USG_PORT); fprintf(stderr, /* usage */ USG_MASTER); fprintf(stderr, /* usage */ USG_SLAVE); #endif /* explain each flag in turn */ fprintf(stderr, /* usage */ USG_OPTS); fprintf(stderr, /* usage */ USG_F_FLG); fprintf(stderr, /* usage */ USG_N_FLG); fprintf(stderr, /* usage */ USG_X_FLG); fprintf(stderr, /* usage */ USG_P_FLG); fprintf(stderr, /* usage */ USG_S_FLG); fprintf(stderr, /* usage */ USG_TT_FLG); fprintf(stderr, /* usage */ USG_O_FLG); fprintf(stderr, /* usage */ USG_H_FLG); fprintf(stderr, /* usage */ USG_C_FLG); fprintf(stderr, /* usage */ USG_K_FLG); fprintf(stderr, /* usage */ USG_IK_FLG); fprintf(stderr, /* usage */ USG_U_FLG); fprintf(stderr, /* usage */ USG_W_FLG); #ifndef BSDPTY fprintf(stderr, /* usage */ USG_M_FLG); #endif #ifdef AUTOPUSH fprintf(stderr, /* usage */ USG_A_FLG); #endif fprintf(stderr, /* usage */ USG_CF_FILE, PROGNAME, CONF_FILE); return; } /* * RHW160892 version 1.7 modification - function to scan network input for * TELNET protocol sequences. The sequence to look for is * * IAC