/* a telnet server (using the telnet protocol) analogous to the unix talk command started: 26 Nov 94 by Roger Espel Llima latest modification: 18 Feb 95 full-screen handling assumes ANSI/VT compatible terminal on both sides; the sequenes used are: ESC[n;mH (gotoxy), ESC[J (clrscreen), ESC[K (clreol) you NEED an ANSI C compiler for this. prehistoric /bin/cc's won't work. to compile on non-braindead Unix: gcc telserv.c -o telserv to compile on Solaris: gcc telserv.c -o telserv -lsocket -lnsl (or even cc or /usr/ucb/cc, if you don't mind the warnings) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _AIX #include #endif /* some defines for telnet */ #define T_IP 244 #define T_BRK 243 #define T_EC 247 #define T_EL 248 #define T_SB 250 #define T_WILL 251 #define T_WONT 252 #define T_DO 253 #define T_DONT 254 #define T_IAC 255 #define T_SGA 3 #define T_ECHO 1 unsigned char initmodes[] = { T_IAC, T_WILL, T_ECHO, T_IAC, T_WILL, T_SGA, T_IAC, T_DO, T_SGA }; #define DEF_PORT 7435 #define BUF_SIZE 300 unsigned char *statuslines[2]; int winsizes[2]; /* 0 = upper, 1 = lower ; lines for the window */ int yfirst[2]; /* 1st y for each window */ int ystatus[2]; /* y for each status line */ int xcursors[2]; /* cursors relative to the windows */ int ycursors[2]; /* start = 0,0 */ int cursorwhere=2; /* 0 = at next pos for win 0 1 = at next pos for win 1 2 = neither */ int rwinsizes[2]; /* same things on the remote end */ int ryfirst[2]; int rystatus[2]; int rxcursors[2]; int rycursors[2]; unsigned char *rstatuslines[2]; int ttyfd; struct winsize wsz; struct termios term, term0; int i, rc; int cols, lines; unsigned char t; unsigned char tmpstr[BUF_SIZE], sendbuf[BUF_SIZE], readbuf[BUF_SIZE], *ts, *tr; int n_ts; int connected=0; struct sockaddr_in saddr; struct hostent *hp; int local_port=DEF_PORT; int sv_sock; int cl_sock; fd_set ready, result; int sock_length; /* vars for the dispatcher of telnet options */ int ctrl_last=0; #define rcols 80 #define rlines 24 extern int errno; void rwrite(unsigned char *s, int l) { memcpy(ts, s, l); ts+=l; n_ts+=l; } void rflush() { if (n_ts) write(cl_sock, sendbuf, n_ts); ts=sendbuf; n_ts=0; } void gotoxy(int x, int y) { /* left upper = 1,1 */ sprintf(tmpstr, "\033[%i;%iH", y, x); write(ttyfd, tmpstr, strlen(tmpstr)); } void rgotoxy(int x, int y) { sprintf(tmpstr,"\033[%i;%iH",y,x); rwrite(tmpstr,strlen(tmpstr)); } void clearscreen() { write(ttyfd, "\033[H\033[J", 6); } void rclearscreen() { rwrite("\033[H\033[J", 6); } void cleanupexit(int n, unsigned char *error) { if (connected) { write(cl_sock, "\033[H\033[J", 6); close(cl_sock); } clearscreen(); tcsetattr(ttyfd,TCSADRAIN,&term0); close(ttyfd); if (error!=NULL) fprintf(stderr, "%s\n", error); exit(n); } void got_signal() { cleanupexit(1, "got signal"); } void ignore_signal() { } void settitle(int i, unsigned char *title) { memset(statuslines[i], '-', cols); if (strlen(title)>1),title,strlen(title)); } void rsettitle(int i, unsigned char *title) { memset(rstatuslines[i], '-', rcols); if (strlen(title)>1),title,strlen(title)); } void displaystatus(int i) { gotoxy(1, ystatus[i]); write(ttyfd, statuslines[i], cols); cursorwhere=2; } void rdisplaystatus(int i) { rgotoxy(1, rystatus[i]); rwrite(rstatuslines[i], rcols); cursorwhere=2; } void go(int i) { if (cursorwhere!=i) gotoxy(xcursors[i]+1, ycursors[i]+yfirst[i]); rgotoxy(rxcursors[i]+1, rycursors[i]+ryfirst[i]); cursorwhere=i; } void display(int i, unsigned char t) { /* prints a char at the local screen taking care of control stuff */ if (t=='\n' || t=='\r') { if (++ycursors[i]>=winsizes[i]) ycursors[i]=0; xcursors[i]=0; if (++rycursors[i]>=rwinsizes[i]) rycursors[i]=0; rxcursors[i]=0; cursorwhere=2; go(i); write(ttyfd, "\033[K", 3); rwrite("\033[K", 3); /* erase to end of line */ } else if (t==3) { /* ^C */ cleanupexit(0, "got ^C thru the connection"); } else if (t==21) { /* ^U */ xcursors[i]=0; rxcursors[i]=0; cursorwhere=2; go(i); write(ttyfd, "\033[K", 3); rwrite("\033[K", 3); /* erase to end of line */ } else if (t==8 || t==0x7f) { if (xcursors[i]>0) { xcursors[i]--; cursorwhere=2; go(i); write(ttyfd, " ", 1); } if (rxcursors[i]>0) { rxcursors[i]--; cursorwhere=2; go(i); rwrite(" ", 1); } cursorwhere=2; go(i); } else if (t>=32 && t<255) { go(i); *tmpstr=t; write(ttyfd, tmpstr, 1); rwrite(tmpstr, 1); if (++xcursors[i]>=cols) { if (++ycursors[i]>=winsizes[i]) ycursors[i]=0; xcursors[i]=0; cursorwhere=2; go(i); write(ttyfd, "\033[K", 3); /* erase to end of line */ } if (++rxcursors[i]>=rcols) { if (++rycursors[i]>=rwinsizes[i]) rycursors[i]=0; rxcursors[i]=0; cursorwhere=2; go(i); rwrite("\033[K", 3); /* erase to end of line */ } } rflush(); } void dispatch(unsigned char c) { if (ctrl_last) { switch(ctrl_last) { case T_IAC: switch(c) { case T_IAC: ctrl_last=0; display(1, T_IAC); break; case T_WILL: ctrl_last=T_WILL; break; case T_WONT: ctrl_last=T_WONT; break; case T_DO: ctrl_last=T_DO; break; case T_DONT: ctrl_last=T_DONT; break; case T_IP: cleanupexit(0, "got Intr thru connection"); case T_BRK: cleanupexit(0, "got BRK thru connection"); case T_EC: ctrl_last=0; display(1, 8); break; } break; case T_DO: case T_WILL: case T_WONT: ctrl_last=0; break; case T_DONT: ctrl_last=0; if (c==T_ECHO) { strcpy(tmpstr, "\r\nCan't set remote ECHO mode, aborting\r\n"); write(cl_sock, tmpstr, strlen(tmpstr)); cleanupexit(1, "Can't set remote ECHO mode"); } } } else { if (c==T_IAC) ctrl_last=T_IAC; else display(1, c); } } int main(int argc, unsigned char **argv) { char *myname; int on = 1; int reuse = 0; myname=*argv; if (argc>1 && strcmp(argv[1], "-r")==0) { reuse++; argc--; argv++; } if (argc>1) { if (argc>2) { fprintf(stderr, "Use: %s [-r ] [port number]\n", myname); exit(1); } if ((local_port=atoi(argv[1]))<1024) { fprintf(stderr, "Invalid port number\n"); exit(1); } } ts=sendbuf; n_ts=0; saddr.sin_family=AF_INET; saddr.sin_addr.s_addr=htonl(INADDR_ANY); saddr.sin_port=htons(local_port); if ((sv_sock=socket(PF_INET, SOCK_STREAM, 0))<0) { fprintf(stderr, "Can't create listening socket\n"); exit(1); } reuse && setsockopt(sv_sock, SOL_SOCKET, SO_REUSEADDR, (char*) &on, sizeof(on)); if (bind(sv_sock, (void *)&saddr, sizeof(saddr))<0) { fprintf(stderr, "Can't bind to local socket\n"); exit(1); } if ((ttyfd=open("/dev/tty",O_RDWR,0666))<0) { fprintf(stderr,"Can't open terminal!\n"); exit(1); } if (ioctl(ttyfd,TIOCGWINSZ,&wsz)<0) { cols=80; lines=24; } else { lines=wsz.ws_row; cols=wsz.ws_col; } if (tcgetattr(ttyfd,&term)<0) { perror("tcgetattr"); exit(1); } if (tcgetattr(ttyfd,&term0)<0) { perror("tcgetattr"); exit(1); } term.c_lflag &= ~ECHO & ~ICANON; term.c_cc[VTIME]=(unsigned char)0; term.c_cc[VMIN]=(unsigned char)1; if (tcsetattr(ttyfd,TCSANOW,&term)<0) { perror("tcsetattr"); exit(1); } signal(SIGHUP, got_signal); signal(SIGINT, got_signal); signal(SIGQUIT, got_signal); signal(SIGPIPE, got_signal); signal(SIGTSTP, ignore_signal); if (lines<2 || cols<2) { lines=24; cols=80; } if (lines>255) { fprintf(stderr,"Screen too big\n"); exit(1); } --lines; winsizes[0]=(lines-1)>>1; winsizes[1]=(lines-2)>>1; ystatus[0]=1; ystatus[1]=winsizes[0]+2; yfirst[0]=2; yfirst[1]=ystatus[1]+1; rwinsizes[0]=(rlines-1)>>1; rwinsizes[1]=(rlines-2)>>1; rystatus[0]=1; rystatus[1]=rwinsizes[0]+2; ryfirst[0]=2; ryfirst[1]=rystatus[1]+1; if (winsizes[1]<=0 || cols<10) { fprintf(stderr, "Screen too small!"); exit(1); } statuslines[0]=malloc(cols); statuslines[1]=malloc(cols); rstatuslines[0]=malloc(rcols); rstatuslines[1]=malloc(rcols); settitle(0, " orabidoo's telnet talk server "); sprintf(tmpstr, " waiting for connection on port %i ", local_port); settitle(1, tmpstr); rsettitle(0, " orabidoo's telnet talk server "); strcpy(tmpstr, " "); gethostname(tmpstr+1, rcols-1); strcat(tmpstr, " "); rsettitle(1, tmpstr); xcursors[0]=xcursors[1]=0; ycursors[0]=ycursors[1]=0; rxcursors[0]=rxcursors[1]=0; rycursors[0]=rycursors[1]=0; clearscreen(); displaystatus(0); displaystatus(1); gotoxy(1,2); listen(sv_sock, 1); sock_length=sizeof(saddr); while ((cl_sock=accept(sv_sock, (void *)&saddr, &sock_length))<0) { if (errno==EINTR) continue; else { perror("accept"); cleanupexit(1, "Weird connection"); } } write(ttyfd, "\007", 1); close(sv_sock); connected++; strcpy(tmpstr, " "); hp=gethostbyaddr((char *)&(saddr.sin_addr.s_addr), sizeof(saddr.sin_addr), AF_INET); if (hp==NULL) { strcat(tmpstr, inet_ntoa(saddr.sin_addr)); } else { strcat(tmpstr, hp->h_name); } strcat(tmpstr, " "); settitle(1, "connected"); settitle(1, tmpstr); displaystatus(1); FD_ZERO(&ready); FD_SET(ttyfd, &ready); FD_SET(cl_sock, &ready); write(cl_sock, initmodes, sizeof(initmodes)); rclearscreen(); rdisplaystatus(0); rdisplaystatus(1); go(0); rflush(); while(1) { result=ready; if (select(64, &result, NULL, NULL, NULL)<=0) if (errno==EINTR) continue; else cleanupexit(1, "select error"); if (FD_ISSET(cl_sock, &result)) if ((rc=read(cl_sock, readbuf, BUF_SIZE))>0) for (tr=readbuf;rc>0;rc--) dispatch(*(tr++)); else cleanupexit(1, "read error from socket"); if (FD_ISSET(ttyfd, &result)) if (read(ttyfd, &t, 1)>0) display(0, t); else cleanupexit(1, "read error from keyboard"); } }