int tn_ini( void ) { sgaflg = 0; /* SGA flag starts out this way. */ if (send_iac( WILL, TELOPT_TTYPE )) return( -1 ); if (send_iac( DO, TELOPT_SGA )) return( -1 ); /* * The ECHO negotiations are not necessary for talking to full-duplex * systems, and they don't seem to do any good when sent to half-duplex * ones -- they still refuse to echo, and what's worse, they get into * prolonged negotiation loops. Real telnet sends only the two above * at the beginning of a connection. */ if (send_iac(WONT,TELOPT_ECHO)) return( -1 ); /* I won't echo. */ if (send_iac(DO,TELOPT_ECHO)) return( -1 ); /* Please, you echo. */ return(0); }
int tn_sttyp(void) { /* Send telnet terminal type. */ char *ttn; int ttl; /* Name & length of terminal type. */ ttn = termtype; /* we already got this from environment */ if ((*ttn == 0) || ((ttl = strlen(ttn)) >= TSBUFSIZ)) { ttn = "UNKNOWN"; ttl = 7; } ttn = strcpy(&sb[1],ttn); /* Copy to subnegotiation buffer */ ttn = strchr( strupr(ttn), 0 ); *sb = 0; /* 'is'... */ *ttn++ = IAC; *ttn = SE; if (send_iac(SB,TELOPT_TTYPE)) /* Send: Terminal Type */ return(-1); sock_fastwrite( s, sb, ttl+3 ); return(0); }
/* We rely on that there is space in the buffer for now. */ char *b = ts->buf2 + ts->rdidx2; *b++ = IAC; *b++ = command; *b++ = option; ts->rdidx2 += 3; ts->size2 += 3; } static struct tsession * #ifdef CONFIG_FEATURE_TELNETD_INETD make_new_session(void) #else /* CONFIG_FEATURE_TELNETD_INETD */ make_new_session(int sockfd) #endif /* CONFIG_FEATURE_TELNETD_INETD */ { struct termios termbuf; int pty, pid; char tty_name[32]; struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2); ts->buf1 = (char *)(&ts[1]); ts->buf2 = ts->buf1 + BUFSIZE; #ifdef CONFIG_FEATURE_TELNETD_INETD ts->sockfd_read = 0; ts->sockfd_write = 1; #else /* CONFIG_FEATURE_TELNETD_INETD */ ts->sockfd = sockfd; #endif /* CONFIG_FEATURE_TELNETD_INETD */ ts->rdidx1 = ts->wridx1 = ts->size1 = 0; ts->rdidx2 = ts->wridx2 = ts->size2 = 0; #ifdef CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT /* initialize it with the timeout value*/ ts->timeout_time = time(NULL) + TIMEOUT; #endif /* CONFIG_FEATURE_TELNETD_INACTIVE_TIMEOUT */ /* Got a new connection, set up a tty and spawn a shell. */ pty = getpty(tty_name); if (pty < 0) { syslog(LOG_ERR, "All network ports in use!"); return 0; } if (pty > maxfd) maxfd = pty; ts->ptyfd = pty; /* Make the telnet client understand we will echo characters so it * should not do it locally. We don't tell the client to run linemode, * because we want to handle line editing and tab completion and other * stuff that requires char-by-char support. */ send_iac(ts, DO, TELOPT_ECHO); send_iac(ts, DO, TELOPT_NAWS); send_iac(ts, DO, TELOPT_LFLOW); send_iac(ts, WILL, TELOPT_ECHO); send_iac(ts, WILL, TELOPT_SGA); if ((pid = fork()) < 0) { syslog(LOG_ERR, "Can`t forking"); } if (pid == 0) { /* In child, open the child's side of the tty. */ int i; for(i = 0; i <= maxfd; i++) close(i); /* make new process group */ setsid(); if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) { syslog(LOG_ERR, "Could not open tty"); exit(1); } dup(0); dup(0); tcsetpgrp(0, getpid()); /* The pseudo-terminal allocated to the client is configured to operate in * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ tcgetattr(0, &termbuf); termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ termbuf.c_oflag |= ONLCR|XTABS; termbuf.c_iflag |= ICRNL; termbuf.c_iflag &= ~IXOFF; /*termbuf.c_lflag &= ~ICANON;*/ tcsetattr(0, TCSANOW, &termbuf); print_login_issue(issuefile, NULL); /* exec shell, with correct argv and env */ execv(loginpath, (char *const *)argv_init); /* NOT REACHED */ syslog(LOG_ERR, "execv error"); exit(1); } ts->shell_pid = pid; return ts; }
static int read_pty(int minor) { /* Characters written to the client side*/ unsigned char value; unsigned int omod; int count; int result; pty_t *pty=telnet_ptys+minor; count=read(pty->socket,&value,sizeof(value)); if (count<0) return -1; if (count<1) { /* Unfortunately, there is no way of passing an EOF * condition through the termios driver. Hence, we * resort to an ugly hack. Setting cindex>ccount * causes the termios driver to return a read count * of '0' which is what we want here. We leave * 'errno' untouched. */ pty->ttyp->cindex=pty->ttyp->ccount+1; return pty->ttyp->termios.c_cc[VEOF]; }; omod=pty->iac_mode; pty->iac_mode=0; switch(omod & 0xff) { case IAC_ESC: switch(value) { case IAC_ESC : /* in case this is an ESC ESC sequence in SB mode */ pty->iac_mode = omod>>8; return IAC_ESC; case IAC_DONT: case IAC_DO : case IAC_WONT: case IAC_WILL: pty->iac_mode=value; return -1; case IAC_SB : #if DEBUG & DEBUG_DETAIL printk("SB\n"); #endif pty->iac_mode=value; pty->sb_ind=0; return -100; case IAC_GA : return -1; case IAC_EL : return 0x03; /* Ctrl-C*/ case IAC_EC : return '\b'; case IAC_AYT : write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP)); return -1; case IAC_AO : return -1; case IAC_IP : write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP)); return -1; case IAC_BRK : write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP)); return -1; case IAC_DMARK: return -2; case IAC_NOP : return -1; case IAC_SE : #if DEBUG & DEBUG_DETAIL { int i; printk("SE"); for (i=0; i<pty->sb_ind; i++) printk(" %02x",pty->sb_buf[i]); printk("\n"); } #endif handleSB(pty); return -101; case IAC_EOR : return -102; default : return -1; }; break; case IAC_SB: pty->iac_mode=omod; if (IAC_ESC==value) { pty->iac_mode=(omod<<8)|value; } else { if (pty->sb_ind < SB_MAX) pty->sb_buf[pty->sb_ind++]=value; } return -1; case IAC_WILL: if (value==34){ send_iac(minor,IAC_DONT, 34); /*LINEMODE*/ send_iac(minor,IAC_DO , 1); /*ECHO */ } else if (value==31) { send_iac(minor,IAC_DO , 31); /*NAWS */ #if DEBUG & DEBUG_DETAIL printk("replied DO NAWS\n"); #endif } else { send_iac(minor,IAC_DONT,value); } return -1; case IAC_DONT: return -1; case IAC_DO : if (value==3) { send_iac(minor,IAC_WILL, 3); /* GO AHEAD*/ } else if (value==1) { /* ECHO */ } else { send_iac(minor,IAC_WONT,value); }; return -1; case IAC_WONT: if (value==1) { send_iac(minor,IAC_WILL, 1); } else { /* ECHO */ send_iac(minor,IAC_WONT,value); } return -1; default: if (value==IAC_ESC) { pty->iac_mode=value; return -1; } else { result=value; if ( 0 #if 0 /* pass CRLF through - they should use termios to handle it */ || ((value=='\n') && (pty->last_cr)) #endif /* but map telnet CRNUL to CR down here */ || ((value==0) && pty->last_cr) ) result=-1; pty->last_cr=(value=='\r'); return result; }; }; /* should never get here but keep compiler happy */ return -1; }
static struct tsession * make_new_session(int sockfd) { struct termios termbuf; int pty, pid; static char tty_name[32]; struct tsession *ts = (struct tsession *)malloc(sizeof(struct tsession)); int t1, t2; #ifdef USE_ISSUE FILE *fp; int chr; #endif ts->buf1 = (char *)malloc(BUFSIZE); ts->buf2 = (char *)malloc(BUFSIZE); ts->sockfd = sockfd; ts->rdidx1 = ts->wridx1 = ts->size1 = 0; ts->rdidx2 = ts->wridx2 = ts->size2 = 0; /* Got a new connection, set up a tty and spawn a shell. */ pty = getpty(tty_name); if (pty < 0) { fprintf(stderr, "All network ports in use!\n"); return 0; } if (pty > maxfd) maxfd = pty; ts->ptyfd = pty; /* Make the telnet client understand we will echo characters so it * should not do it locally. We don't tell the client to run linemode, * because we want to handle line editing and tab completion and other * stuff that requires char-by-char support. */ send_iac(ts, DO, TELOPT_ECHO); send_iac(ts, DO, TELOPT_LFLOW); send_iac(ts, WILL, TELOPT_ECHO); send_iac(ts, WILL, TELOPT_SGA); if ((pid = fork()) < 0) { perror("fork"); } if (pid == 0) { /* In child, open the child's side of the tty. */ int i, t; for(i = 0; i <= maxfd; i++) close(i); /* make new process group */ if (setsid() < 0) perror_msg_and_die("setsid"); t = open(tty_name, O_RDWR | O_NOCTTY); //t = open(tty_name, O_RDWR); if (t < 0) perror_msg_and_die("Could not open tty"); t1 = dup(0); t2 = dup(1); tcsetpgrp(0, getpid()); if (ioctl(t, TIOCSCTTY, NULL)) { perror_msg_and_die("could not set controlling tty"); } /* The pseudo-terminal allocated to the client is configured to operate in * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */ tcgetattr(t, &termbuf); termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */ termbuf.c_oflag |= ONLCR|XTABS; termbuf.c_iflag |= ICRNL; termbuf.c_iflag &= ~IXOFF; /* termbuf.c_lflag &= ~ICANON; */ tcsetattr(t, TCSANOW, &termbuf); DEBUG_OUT("stdin, stdout, stderr: %d %d %d\n", t, t1, t2); #ifdef USE_ISSUE /* Display ISSUE_FILE */ if ((fp = fopen(ISSUE_FILE, "r")) != NULL) { DEBUG_OUT(" Open & start display %s\n", ISSUE_FILE); while ((chr=fgetc(fp)) != EOF) { if (chr == '\n') fputc('\r', stdout); fputc(chr, stdout); } fclose(fp); } #endif /* exec shell, with correct argv and env */ execv(loginpath, argv_init); /* NOT REACHED */ perror_msg_and_die("execv"); } ts->shell_pid = pid; return ts; }
int tn_doop(int c) { int x, y, n, flag; x = ttinc(0) & 0xff; /* Read command character */ switch (x) { case TELOPT_ECHO: /* ECHO negotiation. */ if (c == WILL) { /* Host says it will echo. */ if (echo) { /* Only reply if change required */ echo = 0; if (send_iac(DO,x)) /* Please do. */ return(-1); } return(0); } if (c == WONT) { /* Host says it won't echo. */ if (!echo) { /* If I'm not echoing already */ if (send_iac(DONT,x)) /* agree to echo. */ return(-1); echo = 1; } return(0); } if (c == DO) { /* Host wants me to echo */ if (send_iac(WONT,x)) /* I say I won't, */ return(-1); if (send_iac(DO,x)) /* and ask the host to echo. */ return(-1); echo = 0; return( 0 ); } if (c == DONT) { /* Host wants me not to echo */ if (send_iac(WONT,x)) /* I say I won't. */ return(-1); echo = 0; return( 0 ); } return(0); case TELOPT_SGA: /* Suppress Go-Ahead */ if (c == WONT) { /* Host says it won't. */ sgaflg = 1; /* Remember. */ if (!echo) { /* If we're not echoing, */ if (send_iac(DONT,x)) /* acknowledge, */ return(-1); echo = 1; /* and switch to local echo. */ } } if (c == WILL) { /* Host says it will. */ sgaflg = 0; /* Remember. */ if (echo) { /* If I'm echoing now, */ if (send_iac(DO,x)) /* this is a change, so ACK. */ return(-1); if (send_iac(DO,TELOPT_ECHO)) /* Request remote echo */ return(-1); } } return(0); case TELOPT_TTYPE: /* Terminal Type */ switch (c) { case DO: /* DO terminal type. */ if (send_iac(WILL,x)) /* Say I'll send it if asked. */ return(-1); return(0); /* enter subnegociations */ case SB: n = flag = 0; /* Flag for when done reading SB */ while (n < TSBUFSIZ) { /* Loop looking for IAC SE */ if ((y = ttinc(0)) < 0) return(-1); y &= 0xff; /* Make sure it's just 8 bits. */ sb[n++] = y; /* Save what we got in buffer. */ if (y == IAC) { /* If this is an IAC */ flag = 1; /* set the flag. */ } else { /* Otherwise, */ if (flag && y == SE) /* if this is SE which immediately */ break; /* follows IAC, we're done. */ else flag = 0; /* Otherwise turn off flag. */ } } if (!flag) return(-1); /* Make sure we got a valid SB */ if ( *sb == 1 ) { if ( tn_sttyp() ) return(-1); }; default: /* Others, ignore */ return(0); } default: /* All others: refuse */ switch(c) { case WILL: /* You will? */ if (send_iac(DONT,x)) /* Please don't. */ return(-1); break; case DO: /* You want me to? */ if (send_iac(WONT,x)) /* I won't. */ return(-1); if (send_iac(DONT,x)) /* Don't you either. */ return(-1); break; case DONT: /* (fall thru...) */ if (send_iac(WONT,x)) /* I won't. */ return(-1); case WONT: /* You won't? */ break; /* Good. */ } return(0); } }