/* * telnet.h * *함수 선언. *by 전영준 */
/* telnet.c 에서 정의된 함수*/ void peer_died(); void do_bye(); void sys_error();
void putc_terminal(int.c); int getc_socket(int*c); void putc_socket(int c); void puts_socket(char*);
int set_terminal(int set); void nonblock(int onoff);
void debug(const char*, ...);
/* protocol.c 에서 정의된 함수*/ void int_telnet(int istelnet); void process_protocol(void);
/*the end of telnet*/
/* *telnet.c * *by 전영준 */
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <stdarg.h>
#include <sys/time.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h>
#include <termios.h> #include <sys/ioctl.h>
#include <arpa/telnet.h>
#include "telnet.h"
#define MAX_WRITE_BUF PIPE_BUF*2 #define MAX_READ_BUF PIPE_BUF
int do_debug = 0;
char hostname[MAXHOSTNAMELEN] int port; int sock;
struct buffer { char buf; /*데이터를 저장할 주소공간*/ int size; /*buf에 할당된 메모리의 크기*/ int head, tail; /*buf에 저장된 데이터의 처음과 끈의 인덱스*/ int count; /*buf에 저장된 데이터의 byte수*/ };
struct buffer write_buf, read_buf;
void init_system(void);
void main_loop(); void read_socket(); void read_terminal(); void write_socket();
void usage() { fprintf(stdeer,"usage; mtelnet[-g] [hostname [port]]\n"); exit(1); }
void main(int argc, char *argv[]) { struct hostent *host; struct sockaddr_ serv_addr; int c; extern int optind;
while((c = getopt(argc, argv, "g"))!=EOF) { switch(c ) { case 'g'; do_debug = 1; break; default; usage(); } }
argc-=optind; argv+=optind;
if(argc <=0||argc>2) usage(); if(argc==2) port=atoi(argv[1]); else port=23;
bzero((char*)&serv_addr,sizeof(serv_addr)); serv_addr.sin_family=AF_INET; serv_addr.sin_port =htons(port);
if((serv_addr.sin_addr.s_addr=inet_addr(argv[0]))!=INADDR_NONE) { strcpy(hostname,arv[0]); } else { if((host=gethostbyname(argv[0]))==NULL) { herror("gethostbyname"); exit(1); } serv_addr.sin_family=host->h_addrtype; bcopy(host->h_addr,(char*) &serv_addr.sin_addr,host->h_length); strcpy(hostname, host->h_name); }
if((sock=socket(AF_INET,SOCK_STREAM,0))<0) { perror("socket"); exit(1); } printf("Connected to %s.\n", hostname);
init_system(void) { write_buf.buf = (char*) malloc(MAX_WRITE_BUF); write_buf.size = MAX_WRITE_BUF; write_buf.head = write_buf.tail = 0; write_buf.count = 0;
read_buf.buf = (char*) malloc(MAX_READ_BUF); read_buf.size = MAX_READ_BUF; read_buf.head = read_buf.tail = 0; read_buf.count = 0;
signal(SIGPIPE, peer_died); nonblock(1); set_terminal(1); }
void peer_died(void) { close(sock); set_terminal(0);
free(write_buf.buf); free(read_buf.buf);
fprintf(stderr,"Connection closed by foreign host.\n"); exit(1); }
void do_bye() { close(sock); set_terminal(0);
free(write_buf.buf); free(read_buf.buf);
printf("Connection closed.\n"); exit(0); }
void sys_error() { close(sock); set_terminal(0);
free(write_buf.buf); free(read_buf.buf);
exit(1); }
void main_loop() { int maxfd; fd_set in_set, out_set, exc_set; int ret;
for(;;) { FD_ZERO(&in_set); FD_ZERO(&out_set); FD_ZERO(&exc_set); FD_SET(sock, &in_set); If(write_buf.count>0) FD_SET(sock, &out_set); FD_SET(0, &in_set); maxfd = sock; /* *디스크립터에 데이터가 들어올때까지 계속 기다리게 하기 위하여 *timeout을 NULL로 지정 하였다. */ ret = select(maxfd + 1, &in_set, &out_set, &exc_set, NULL); if(ret<0) { perror("select"); sys_error(); } /*키보드로부터의 입력을 읽는다.*/ if(FD_ISSET(0, &in_set)) { FD_CLR(sock, &in_set); read_terminal(); } /*소켓에 데이터를 쓴다.*/ if(FD_ISEET(sock, &out_set)) { F
D_DLR(sock, &out_set); Write_socket(); } /*소켓으로부터 데이터를 읽는다.*/ if(FD_ISSET(sock, &in_set)) { FD_CLR(sock, &in_set); read_socket(); } /*소켓에서부터 읽은 데이터가 있으면 그것을 처리한다.*/ if(read_buf.count>0) { process_protocol(); } } } /*소켓에서 데이터를 읽어서 read_bif에 저장한다. */ void read_socket() { int n;
if(read_buf.size == read_buf.tail) return;/*read_buf에 여유공간이 없다. */
n=read(sock, read_buf.buf+read_buf.tail, read_buf.size_read_buf.tail); if(n< 0 && errno ==EWOULDBLOCK) n = 0; if(n<0) { perror("read"); sys_error(); } if (n ==0) peer_died();
read_buf.count +=n; read_buf.tail +=n; }
/*키보드로부터 입력을 받아서 write_buf에 저장한다.*/ void read_terminal() { int n;
if (write_buf.size ==write_buf.tail) return;
n = read(0, write_buf.buf+write_buf.tail, write_buf.size-write_buf.tail); if(n<0 &&errno ==EWOULDBLOCK) n = 0; if(n<0) { perror("read"); sys_error(); } if(n ==0) { do_bye(); }
write_buf.count+= n; write_buf.tail+= n;
}
/*write_buf에 있는 데이터를 소켓에 쓴다.*/ void write_socket() { int n;
n = write(sock, write_buf.buf+write_buf.head, write_buf.count); if(n<0&&(errno==ENOBUFS || errno==EWOULDBLOCK)) n = 0; if(n<0) { perror("write"); sys_error(); }
write_buf.head+=n; write_buf.count-=n;
if(write_buf.count==0) write_buf.hea = write_buf.tail = 0;
}
/* *putc_socket(int) *puts_socket(char*) *getc_socket(char*) *putc_terminal(int) *이 함수들은 process_protocol() 수행중 필요한 함수들이다. * *putc_socket()와 puts_socket()는 write_buf에 데이터를 쓰기위한 함수. *getc_socket()는 read_buf에서 데이터를 읽기 위한 함수. *putc_terminal()는 데이터를 화면에 찍기 위한 함수이다. */
void putc_socket(int c) { if(write_buf.tail == write_buf.size) /*write_buf에 여유공간이 없다.*/ write_socket(); if(write_buf.tail ==write_buf.size) { fprintf(stderr,"write buffer full!\n"); return; } write_buf.buf[write_buf.tail++] = c; write_buf.count++; }
void puts_socket(char*s) { int len = strlen(s); if(write_buf.tail + len>write_buf.size) /*write_buf에 여유공간이 없다.*/ write_socket(); if(write_buf.tail + len > write_buf.size) { fprintf(stderr,"write buffer full!\n"); return; } strcpy(write_buf.buf+write_buf.tail, s); write_buf.tail +=len; write_buf.count+=len; }
int getc_socket(int*c) { if(read_buf.count==0) /*더 이상 읽을 것이 없다.*/ return 0;
*c = read_buf.buf[read_buf.head++]; *c = *c & 0xff;
read_buf.count-;
if(read_buf.count ==0) read_buf.head = read_buf.tail =0; return read_buf.count+1; }
void putc_terminal(int c) { int n; n = write(1, &c, 1); if(n<0) { perror("write"); sys_error(); } }
/*소켓을 nonblock 상태로 만든다. */ void nonblock(int onoff) { if(ioctl(sock, FIONBIO, &onoff)<0 { perror("ioctl"); sys_error(); }
}
int set_terminal(int set) { static struct termios save_termios; struct termios buf; static int isset = 0; extern int mode;
if(isset == 0 && set ==0) return 0;
if(set ==0) { if(tcsetattr(0, TCSAFLUSH, &save_termios)<0) return -1; return 0; }
if(isset == 0) if(tcgetattr(0, &save_termios)<0) return-1;
if(tcgetattr(0, &buf)<0) return-1;
buf.c_lflag &=~ISIG; buf.c_oflag |=ONLCR; buf.c_oflag |=OPOST; buf.c_iflag |=ICRNL;
if(mode & MODE_ECHO) { buf.c_flag|=ECHO; buf.c_oflag|=ONLCR;
buf.c_iflag|=ICRNL; buf.c_lflag|=ICANON; } else { buf.c_lflag & =~ECHO; /*buf.c_oflag&=~ONLCR;*/
buf.c_lflag&=~ICANON; /*어떤 시스템에서는 NL(0x0A)을 보내야 한다.*/ /*buf.c_iflag & =~ICRNL;*/ buf.c_cc[VMIN] = 1; buf.c_cc[VTIME] = 0; }
if(mode & MODE_FLOW) buf.c_iflag|=IXANY|IXOFF|IXON; else buf.c_iflag &=~(IXANY|IXOFF|IXON);
if (mode & MODE_INBIN) buf.c_iflag & =~ISTRIP; else buf.c_iflag |=ISTRIP; if(mode& MODE_OUTBIN) { buf.c_cflag &=~(CSIZE|PARENB); buf.c_cflag|=CS8; /*buf.c_oflag &= ~OPOST;*/ } else { buf.c_cflag &=~(CSIZE|PARENB); buf.c_cflag|=save_termios.c_cflag & (CSIZE|PARENNB); buf.c_oflag|=OPOST; } if(tcsetattr(0, TCSAFLUSH, &buf<0) return-1; isset = set; return 0; }
void debug(const char *msg, ...) { va_list ap;
if(!do_debug) return;
va_start( ap,msg); vfprintf( stderr, msg, ap); va_end(ap); fprintf(stderr, "\n");
}
/*the end of telnet.c*/
/* *protocol.c * *프로토콜 처리를 위한 루틴들 *by 전영준 */
#include <stdlib.h> #include <stdio.h> #include <sys/ioctl.h>
#define TELCMDS #define TELOPTS #include <arpa/telnet.h>
#include "telnet.h"
char options[256]; int mode;
void process_option(int cmd, int opt); void send_option(in cmd, int opt, int); int option_ok(int opt); void clean_sb(); void process_sb(); void send_naws(); void send_ttype(); void mode_set(int m, int set); int option_requested(int opt); void option_request(int opt, int req);
void init_telnet(int istelnet) { int i;
for(i=0;i<256;I++) options[i] = 0;
mode = 0; /*현재 디폴트로 설정되어 있는 모드*/ mode_set(MODE_ECHO,1);
set_terminal(1); /*텔넷접속일 경우에는 텔넷 초기 설정을 한다.*/ if(istelnet) { send_option(DO, TELOPT_SGA, 1); send_option(WILL, TELOPT_TTYPE, 1); send_option(WILL, TELOPT_NAWS, 1); send_option(WILL, TELOPT_LFLOW, 1); send_option(WILL, TELOPT_LINEMODE, 1); send_option(DO, TELOPT_STATUS, 1);
/*enter binarymode*/ send_option(DO, TELOPT_BINARY, 1); send_option(WILL, TELOPT_BINARY, 1); } }
/*read_buf에 저장되어 있는 데이터에서 프로토콜을 찾아서 처리한다.*/ void process_protocol(void) { int c;
while(getc_socket(&c)) { if(c ==IAC) { if(!getc_socket(&c)) return; switch(c) { case IAC: putc_terminal(c); break; case DON'T: case DO: case WONT: case WILL: { int opt; if(!getc_socket(&opt)) return; process_option(c, opt); break; } case SB: if(!getc_socket(&c)) return; if(c ==TELOPT_TTYPE) { if(!getc_socket(&c)) return; clean_sb(); if(c ==TELQUAL_SEND) send_ttype(); break; } clean_sb(); break;
default: break;
} } else { putc_terminal(c); } } }
void clean_sb() { int c; for(;;) { if(!getc_socket(&c)) return; if(c ==IAC) { if(!getc_socket(&c)) return; if(c ==SE) return; } } }
void process_option(int cmd, int opt) { debug("RCVD: IAC %s%s", TELCMD(cmd), TELOPT(opt));
/* If this is an option we do not understand or have not implemented, refuse any 'DO' request. */ if(!option_ok(opt)) { if( cmd == DO) send_option(WONT, opt,0); if( cmd == WILL) send_option(DONT, opt,0); } else if(cmd == DO) { switch(opt) { case TELOPT_ECHO: /*never echo if once turned off*/ mode_set(MODE_ECHO, 0); send_option(WONT, opt, 0); goto out_processing; return; case TELOPT_BINARY: mode_set(MODE_OUTBIN, 1); break; case TELOPT_LFLOW: mode_set(MODE_FLOW, 1); break; case TELOPT_NAWS: send_naws(); break; } if(!option_requested(opt))
send_option(WILL, opt, 0); } else if( cmd == DONT) { switch(opt) { case TELOPT_ECHO; mode_set(MODE_ECHO, 0); break; case TELOPT_BINARY: mode_set(MODE_OUTBIN, 0); break; case TELOPT_LFLOW: mode_set(MODE_FLOW, 0); break;
} if(!option_requested(opt)) send_option(WONT, opt, 0); } else if( cmd==WILL) { switch(opt) { case TELOPT_ECHO: mode_set(MODE_ECHO, 0); break; case TELOPT_BINARY: mode_set(MODE_INBIN, 1); break; case TELOPT_LFLOW: mode_set(MODE_FLOW, 1); break;
} if(!option_requested(opt)) send_option(DO, opt, 0); } else if(cmd == WONT) { switch(opt) { case TELOPT_ECHO: mode_set(MODE_ECHO, 1); break; case TELOPT_BINARY: mode_set(MODE_INBIN, 0); break; case TELOPT_LFLOW: mode_set(MODE_FLOW, 0); break; } if(!option_requested(opt)) send_option(DON'T, opt, 0); } out_processing: set_terminal(1); } void send_option(int cmd, int opt, int request) { if(request && !option_ok(opt)) return;
option_request(opt, request);
debug("SENT:IAC%s%s", TELCMD(cmd), TELOPT(opt)); putc_socket(IAC); putc_socket(cmd); putc_socket(opt); }
/*터미널 타입*/ void send_ttype() { char s[50]; strcpy(s, (getenv("TERM") ==NULL? "UNKNOWN" : getenv("TERM")));
putc_socket(IAC); putc_socket(SB); putc_socket(TELOPT_TTYPE); putc_socket(TELQUAL_IS); putc_socket(s); putc_socket(IAC); putc_socket(SE);
debug("SENT: IAC SB TELOPT_TTYPE IS\"%s\" IAC AE" ,s);
}
/*터미널 윈도우 크기*/ void send_naws() { char s[50]; struct winsize size; if(ioctl(0, TIOCGWINSZ, (char*)&size)<0) { perror("ioctl"); sys_error(); } s[0] = (size.ws_col>>8)&0xFF; s[1] = (size.ws_col&0xFF); s[2] = (size.ws_row>>8)&0xFF; s[3] = (size.ws_row&0xFF); s[4] = 0; putc_socket(IAC); putc_socket(SB); putc_socket(TELOPT_NAWS); putc_socket(s); putc_socket(IAC); putc_socket(SE);
debug("SENT:IAC SB TELOPT_NAWS%d%d%d%dIAC SE", s[0],s[1],s[2],s[3]); }
int option_ok(int opt) { if(opt == TELOPT_ECHO||opt==TELOPT_BINARY||opt==TELOPT_SGA|| opt == TELOPT_LFLOW||opt==TELOPT_TTYPE||opt==TELOPT_NAWS) return 1; else return 0; }
void mode_set(int m, int set) { if(set) mode |=m; else mode &=~m; }
int option_requested(int opt) { return option[opt]>0; }
void option_request(int opt, int req) { if(req>0) options[opt]++; else if(options[opt]>0) options[opt]-;
}
/*the end of protocol.c*/ 다음은 이 프로그램을 위한 Makefile이다. telnet.h, telnet.c, protocol.c를 각각 저장한 뒤 같은 디렉토리에 다음의 Makefile을 저장하여 make하면 된다.
#Makefile for mtelnet CC = cc DEBUG = O_FLAGS= C_FLAGS=$(O_FLAGS)-Wall$(DEBUG) L_FLAGS=$(O_FLAGS)
O_FILES = mtelnet $(TARGET):$(O_FILES) $(CC)$(L_FLAGS)-o$(TARGET)$(O_FILES)
.c.o: $(CC)$-c$(C_FLAGS)$<
clean: rm*.o$(TARGET) |