/* *
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) |