TCP/IPプログラミング

055710C 大城絢子


課題1

telnetコマンドによるWWWサーバへのアクセス telnetコマンドを使って任意のWWWサーバにアクセスし、任意のURLのページデータ(htmlソースプログラム)を画面に表示せよ。(報告書にはそのURLとページデータの先頭の20行程度を添付せよ)

nw0510:/Users/j05010>telnet www.glay.co.jp 80
Trying 133.163.1.32...
Connected to www.glay.co.jp.
Escape character is '^]'.
GET/index.html HTTP/1.0
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>GLAY OFFICIAL WEBSITE [ NOT FOUND ]</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
< style type="text/css" >

</style
</head>
<body bgcolor="#ffffff" text="#000000">
<table border="0" align="center">
<tr>
<td width="600"><a href="http://www.glay.co.jp/" target="_parent"><img src="/error/img/tit.gif" width="152" height="58" alt="glay" align="middle" border="0"></a> <font size="4"><b>wy[W</b></font>
<hr size="1">
<font size="2">URLAYy[W\</font><br>
<br>
<img src="/error/img/yj.gif" width="14" height="16" alt="" align="middle"><font size="2">GLAY OFFICIAL WEBSITENNbN\A<br>
      Ahttp://ny[WAhXLA<a href="mailto:support-glay@m-up.com?subject=GLAY%83%8A%83%93%83N%82%CC%8A%D4%88%E1%82%A2%82%C9%82%C2%82%A2%82%C4">support-glay@m-up.com</a>B</font><br>
<hr size="1">
<table cellpadding="1" cellspacing="1" border="0">
<tr>
<td width="600" bgcolor="#ffcc00"><font size="2">GLAY HAPPY SWING MEMBERS SITE</font></td>
</tr>


課題2

 inetdを使用するサーバプログラムの作成 サンプルプログラム(1)はサーバ(server.c)をinetdから起動することで、サーバはクライアント(client.c)の標準入力から入力された文字に対応するキーワードをクライアントに返すプログラムである。サンプルプログラム(1)を自分の実験環境で動作するようにし、実行結果を示すとともに、プログラムの中で使われている関数(Connect,Disconnect, Send Data, Recv Dataなど)の動作を説明し、サーバ・クライアント動作全体をフローを示して説明せよ。


nw0510:/Users/j05010/kadai1 > ./client 
Connected.
Input Keyword = warning: this program uses gets(), which is unsafe.
yama
Keyword = [yama] / Data = [kawa]

Input Keyword = 123
Keyword = [123] / Data = [456]

Input Keyword = xyz
Keyword = [xyz] / Data = [XYZ]

Input Keyword = abc
Keyword = [abc] / Data = []

Input Keyword = 
Disocnnected.

・Conect:socketの生成をして、host nameやhost addressの設定をして、conectionの準備をする関数
・Disconnect:connnectionをcloseする関数
・Send Data:サーバにデータを送る関数
・Recv Data:サーバからデータを受信する関数
・GetLineFromPeer:クライアントからデータを受信する関数
・GetKeywordData:入力された文字列とDataファイルを比較して、ワードを返す関数

課題3

inetdを使用しないサーバプログラムの作成 サンプルプログラム(1)のサーバプログラム(server.c)はinetdから起動するものであるが、inetdを使用せずに同じ動作をするデーモン型のサーバプログラムを作成し、実行結果を示すとともに、inetdを使用するサーバプログラムとそうでないものとの実装上の違いを説明せよ。


/*** TCP Client ***/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 5524
#define	PROTOCOL	"tcp"
#define	HOST_NAME	"nw0510"

int	Connect()
{
int	SocketNumber;		/* Socket descripter */
struct	hostent	*HostEntry;	/* host entry */
struct	sockaddr_in	sin;	/* Socket Entry */

/* Get Host Entry by Host-Name */

if((struct hostent *)NULL == (HostEntry = gethostbyname(HOST_NAME)))
	{
	printf("\7No Hosts [%s].\n",HOST_NAME);
	exit(1);
	}

/* Get Socket */

if(0 > (SocketNumber = (socket(PF_INET,SOCK_STREAM,0))))
	{
	printf("\7Cannot get Socket.\n");
	exit(1);
	}


bzero((char *)(&sin), sizeof(sin));

bcopy(HostEntry->h_addr,&sin.sin_addr,HostEntry->h_length);
sin.sin_family = PF_INET;
sin.sin_port = htons(PORT);


if(0 > connect(SocketNumber,(struct sockaddr *)(&sin),sizeof(sin)))
	{
	printf("\7Cannot Connect.\n");
	exit(1);
	}

return SocketNumber;
}

void	Disconnect(int SocketNumber)
{
close(SocketNumber);
}

void	SendData(int SocketNumber,char *line)
{
if(strlen(line) != write(SocketNumber,line,strlen(line)))
	{
	printf("\7Send Failed.\n");
	exit(1);
	}
}

int	RecvData(int SocketNumber,char *line)
{
int	i;

for(i = 0 ;;)
	{
	if(1 != read(SocketNumber,line + i,1))
		continue;
	else
		{
		i++;
		*(line + i) = (char)0;
	
		if(((char)('\n')) == *(line + i - 1))
			return i;
		}
	}
}

int	main()
{
int	Sock;
char	Key[256];
char	Data[256];
char	buff[256];

Sock = Connect();	/* Connect */
printf("Connected.\n");

for(;;)
	{
	char	*p;

	printf("Input Keyword = ");
	fflush(stdout);

	gets(Key);
	sprintf(buff,"%s=\n",Key);
	SendData(Sock,buff);		/* Send It ! */
	if(0 == strlen(Key))
		break;			/* If End Mark */

	/* ---- Waiting for Server response Here --- */

	(void)RecvData(Sock,buff);		/* Recv It ! */

	if((char *)NULL != (p = strchr(buff,'\n')))
		*p = (char)0;

	if((char *)NULL != (p = strchr(buff,'=')))
		p++;

	printf("Keyword = [%s] / Data = [%s]\n\n",Key,p);
	}

Disconnect(Sock);
printf("Disocnnected.\n");
}




/*** TCP Server ***/

#include <stdio.h >
#include  <stdlib.h>
#include  <sys/types.h>
#include  <sys/errno.h>
#include  <string.h>
#include  <ctype.h>
#include  <sys/socket.h>
#include  <netinet/in.h>
#include  <netdb.h>

#define PORT 5524
#define	DATAFILE	"/Users/j05010/kadai1/data.txt"
#define	DELIMITS	" \t\n\r"

char	datavalue[256];


int	GetLineFromPeer(int p,char *line)
{
int	i;

for(i = 0 ; ; )
	{
	if(1 != read(p ,line + i,1))
		continue;
	else
		{
		i++;
		*(line + i) = (char)0;
		}

	if(((char)('\n')) == (*(line + i - 1)))
		return i;
	}
}

char	*GetKeywordData(char *key)
{
FILE	*fp;
char	buff[256];

if((FILE *)NULL == (fp = fopen(DATAFILE,"r")))
	return (char *)NULL;

for(;;)
	{
	char	*p;
	char	*pp;

	fgets(buff,256,fp);

	if(feof(fp))
		break;

	if(ferror(fp))
		break;

	if((char *)NULL == (p = strtok(buff,DELIMITS)))
		continue;

	if((char *)NULL == (pp = strtok((char *)NULL,DELIMITS)))
		continue;

	if(0 == strcmp(p,key))
		{
		fclose(fp);
		strcpy(datavalue,pp);
		return datavalue;
		}
	}

fclose(fp);
return (char *)" ";
}


int	main()
{
char	RecvBuff[256];
   int sockfd;
   int wait_socket;
   int writer_len;
   struct sockaddr_in r_addr; 
   struct sockaddr_in w_addr;
   
   if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 
   perror("socket");
   exit(1);
   }
   
   
   
   bzero((char *) &r_addr, sizeof(r_addr)); 
   r_addr.sin_family = PF_INET;
   r_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   r_addr.sin_port = htons(PORT);
   
   
   if (bind(sockfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0) {
   perror("bind");
   exit(1);
   }
   
   
   if (listen(sockfd, 5) < 0) {
   perror("listen");
   close(sockfd);
   exit(1);
   }
   
   if ((wait_socket = accept(sockfd,(struct sockaddr *)&w_addr, &writer_len)) < 0) {
   perror("accept");
   exit(1);
   }

for(;;)
	{
	char	*p;
	char	SendBuff[256];
	char	*pp;

	(void)GetLineFromPeer(wait_socket,RecvBuff);	/* Get 1 line from peer */

	if(((char)('=')) == *(RecvBuff))	/* If End Mark */
	{
	exit(0);
	}

	if((char *)NULL != (p = strchr(RecvBuff,'=')))
		{
		*p = (char)0;

		if((char *)NULL == (pp = GetKeywordData(RecvBuff))){
		sprintf(SendBuff,"%s=\n",RecvBuff);
		}
		else{
		sprintf(SendBuff,"%s=%s\n",RecvBuff,pp);
		(void)write(wait_socket,SendBuff,strlen(SendBuff));
		}
		}
	}
}





nw0510:/Users/j05010/kadai1 > ./new_server


サーバを起動した後、別のターミナルでnew_clientを実行
nw0510:/Users/j05010/kadai1 > ./new_client 
Connected.
Input Keyword = warning: this program uses gets(), which is unsafe.
yama
Keyword = [yama] / Data = [kawa]

Input Keyword = 123
Keyword = [123] / Data = [456]

Input Keyword = 
Disocnnected.



課題4

HTTPクライアントの作成 ソケットおよびHTMLを使ってWWWサーバから任意のURLのページを取得し、標準出力に出力するプログラムを作成せよ。



/*** TCP Client ***/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 80
#define SERVICE_NAME "http"
#define	PROTOCOL	"tcp"

void	Disconnect(int SocketNumber)
{
close(SocketNumber);
}

void	SendData(int SocketNumber,char *line)
{
if(strlen(line) != write(SocketNumber,line,strlen(line)))
	{
	printf("\7Send Failed.\n");
	exit(1);
	}
}

int	RecvData(int SocketNumber,char *line)
{
int	i;

for(i = 0 ;;)
	{
	if(1 != read(SocketNumber,line + i,1)){
		continue;
	}
	else
		{
		i++;
		*(line + i) = (char)0;
		if(((char)('\n')) == *(line + i - 1))
			return i;
		}
	}
}

int	main()
{
int	Sock;
char	Key[256];
char	Data[256];
char	buff[256];
char    HOST_NAME[256];

int	SocketNumber;		/* Socket descripter */
struct	servent	*ServiceEntry;	/* service entry */
struct	hostent	*HostEntry;	/* host entry */
struct	sockaddr_in	sin;	/* Socket Entry */

/* Get Service Entry by Service-Name */

if((struct servent *)NULL == (ServiceEntry =
	getservbyname(SERVICE_NAME,PROTOCOL)))
	{
	printf("\7No service [%s].\n",SERVICE_NAME);
	exit(1);
	}

/* Get Host Entry by Host-Name */
printf("Input www server = ");
gets(HOST_NAME);
printf("\n");

if((struct hostent *)NULL == (HostEntry = gethostbyname(HOST_NAME)))
	{
	printf("\7No Hosts [%s].\n",HOST_NAME);
	exit(1);
	}

/* Get Socket */

if(0 > (SocketNumber = (socket(PF_INET,SOCK_STREAM,0))))
	{
	printf("\7Cannot get Socket.\n");
	exit(1);
	}


bzero((char *)(&sin), sizeof(sin));

bcopy(HostEntry->h_addr,&sin.sin_addr,HostEntry->h_length);
sin.sin_family = PF_INET;
sin.sin_port = htons(PORT);


if(0 > connect(SocketNumber,(struct sockaddr *)(&sin),sizeof(sin)))
	{
	printf("\7Cannot Connect.\n");
	exit(1);
	}

Sock = SocketNumber;	/* Connect */
printf("Connected.\n");

	char	*p;

	printf("Input Path = ");
	fflush(stdout);

    gets(Key);
    if(0 == strlen(Key)){
	   Disconnect(Sock);
	   printf("Disocnnected.\n");
	   exit(1);
    }

	sprintf(buff,"GET %s HTTP/1.0\r\n",Key);
	SendData(Sock,buff);
	sprintf(buff,"Host: %s:%d\r\n", HOST_NAME, PORT);
	SendData(Sock,buff);
	sprintf(buff,"\r\n");
	SendData(Sock,buff);		/* Send It ! */

	while (1){
	   char buf[256];
       int size;
       size = read(Sock, buf, 256);

	   if ( size > 0 ){
          write(1, buf, size);
        } else {
          break;
        }
	}

Disconnect(Sock);
printf("Disocnnected.\n");
}



nw0510:/Users/j05010/Desktop>./get
warning: this program uses gets(), which is unsafe.
Input www server = www.yahoo.co.jp                             

Connected.
Input Path = /index.html
HTTP/1.1 200 OK
Date: Thu, 14 Dec 2006 07:02:49 GMT
P3P: policyref="http://privacy.yahoo.co.jp/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
Expires: -1
Pragma: no-cache
Cache-Control: no-cache
Connection: close
Content-Type: text/html; charset=euc-jp

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<!--京-->
<title>Yahoo! JAPAN</title>
<meta name="description" content="日本最大級のポータルサイト。検索、オークション、ニュース、メール、コミュニティ、ショッピング、など80以上のサービスを展開。あなたの生活をより豊かにする「ライフ・エンジン」を目指していきます。">
<style type="text/css" media="all">
<!--
.spacer { line-height: 110%; }
.spacer1 {line-height: 115%; }
#h,#hm{display:none}
.newp {background-color:#fffccc;}

#sf1 { margin:0; }
#s { margin:0 0 7px 0; }
#fs { padding:0; margin:0; border:0; }

#v {
display:block;
z-index:5;
position:relative;
width:100%;
height:1px;
border:1px solid #CFCFCF;
border-bottom:0 none #FFFFFF;
}
html>body #v { height:auto; }
:root #v:after {
content:".";
display:block;
font-size:0;
height:0;
clear:both;
visibility:hidden;
}


中略

Copyright (C)
2006 Yahoo Japan Corporation. All Rights Reserved.
</small> </span> 
</center>

<img src="http://pa.yahoo.co.jp/bc?" alt="" width=1 height=1>
</body>

</html>
<!-- p04.f5.top.tnz.yahoo.co.jp uncompressed Thu Dec 14 16:02:49 JST 2006 -->
Disocnnected.


課題5

ポートスキャンの実験 自分の実験環境(端末)の使用/未使用ポート(ウェルノウンポートのみでok)を確認するポートスキャンプログラムを作成せよ。さらに、任意のリモート端末の使用/未使用ポートを確認するように改良せよ(加点ポイント)。なお、スクリプトを使って内部で'netstat -l'コマンドを実行し、その結果を利用するのは不可とする(ソケットプログラムを作成すること)。

/*** TCP Client ***/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>



int	Connect()
{
  int	SocketNumber;		/* Socket descripter */
  int port_num=0;
  char address[256];
  struct	sockaddr_in	sin;	/* Socket Entry */

  printf("Input address : ");
  gets(address);
  printf("\n");

  for(port_num=0; port_num<1024; port_num++){

    if(0 > (SocketNumber = (socket(AF_INET,SOCK_STREAM,0))))
	{
	    printf("\7Cannot get Socket.\n");
	    exit(1);
	}

    /* Set protocol family name */
    sin.sin_family = AF_INET;


    /* Set Port N.o. */
    sin.sin_port = htons(port_num);
    sin.sin_addr.s_addr = inet_addr(address);

	    if(0 > connect(SocketNumber,(struct sockaddr *)(&sin),sizeof(sin))){
			//printf("Port number = %d : don't use\n",port_num);
			
	    }
	    else{
			printf("Port number = %d : use\n",port_num);
			
	    }
	    close(SocketNumber);
    }

  return SocketNumber;
}

int	main(int argc, char *argv)
{
int	Sock;

Sock = Connect(argv[1]);	/* Connect */


printf("\nPort Scan end\n");

printf("Disocnnected.\n");
}


以下に実行結果を示す

nw0510:/Users/j05010/Desktop>./scan
warning: this program uses gets(), which is unsafe.
Input address : 133.13.59.10

Port number = 80 : use
Port number = 427 : use
Port number = 548 : use
Port number = 631 : use

Port Scan end
Disocnnected.



課題6

バッファオーバーフローの実験 サンプルプログラム(2)を実行せよ。このプログラムはgets()関数を用いて、標準入力からの入力をバッファにデータを読み込むものであるが、結果を見ると、プログラム中で操作していないバッファdmy[]に値が入ることがある。この原因を考察し、解決策を示せ。また、この問題によって引き起こされるTCP/IP通信におけるセキュリティ上の欠陥はどのようなものが考えられるか、具体例を挙げて述べよ。 BUFLEN=20として、実行した時の結果を示す。


nw0510:/Users/j05010/Desktop>./bufovf
before
buf(Len:0) = 
dmy(Len:0) = 
warning: this program uses gets(), which is unsafe.
zxsdftyui90op;lkjhgfdesw3456789olkjhgfdsw34567890p@;lkjhgfdsdzxfcgvhbjnkjloi98765trewsdfghjkl;:/.;lkjhgtfrdsdfghjkl
after
buf(Len:115) = zxsdftyui90op;lkjhgfdesw3456789olkjhgfdsw34567890p@;lkjhgfdsdzxfcgvhbjnkjloi98765trewsdfghjkl;:/.;lkjhgtfrdsdfghjkl
dmy(Len:0) = 
Segmentation fault



改善した後のソースを以下に示す

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFLEN 20

int main(int argc, char **argv) {

  char *rtn;
  char dmy[BUFLEN];
  char buf[BUFLEN];

  memset(dmy, '\0', BUFLEN);
  memset(buf, '\0', BUFLEN);

  printf("before\n");

  printf("buf(Len:%d) = %S\n", strlen(buf), buf);
  printf("dmy(Len:%d) = %s\n", strlen(dmy), dmy);

  if ((rtn = fgets(buf,sizeof(buf),stdin) == NULL) {
    exit(-1);
  }

  printf("after\n");
  printf("buf(Len:%d) = %s\n", strlen(buf), buf);
  printf("dmy(Len:%d) = %s\n", strlen(dmy), dmy);

}




実行結果を以下に示す。


nw0510:/Users/j05010/Desktop>./bufovf 
before
buf(Len:0) = 
dmy(Len:0) = 
1^[23ertyhjkl;/:lmnbgfcdxzasdfgyhujiop@^-09754e3wqertyuiop@-p0o9i8uytrewsdfghjk:;lkiujyhtredrftgyhujikop@[
after
buf(Len:19) = 13ertyhjkl;/:lmnb
dmy(Len:0) = 


問題
gets関数はバッファのサイズを越えて書き込み続けてしまう。よって、バッファがオーバーフローしてしまう。
解決策
バッファがオーバーフローすることによって、メモリサイズを越えて、文字列が入力されてしまうのを防ぐために、fgets関数を用いて、文字列のサイズを制限した。