プロセス間通信、TCP/IP(2)

システム・プログラムI

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1997/1997-06-10
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html

■復習

■rdaytime-server

■汎用 TCP/IP クライアントとしての telnet(補足)

^] を打つと、ローカルの telnet コマンドを制御することができる。 ここで、quit などのコマンドが使える。
--------------------------------------------------------------------
% telnet localhost echo [←]
Trying...
Connected to localhost.
Escape character is '^]'.
hello[←]
hello
exit[←]
exit
quit[←]
quit
aaa[←]
aaa
^]
telnet> quit[←]
Connection closed.
% []
--------------------------------------------------------------------

★練習問題(34) pipe() と TCP/IPのストリーム(1)

pipe() の代りに、TCP/IP のストリームを使って、異なる計算機上のプロセス を接続して、フィルタの処理を行わせてみなさい。

★練習問題(35) pipe() と TCP/IPのストリーム(2)

(34) で、3つ以上のプロセスを接続できるようにしなさい。

★練習問題(36) pipe() と TCP/IPのストリーム(3)

(35) で、中間のプログラムとして、外部のフィルタ・プログラムを実行でき るようにしなさい。

■echo-server-fork

TCP/IP のポート番号 7 (echo) では、受け取ったデータをそのまま返すサー ビスを提供している。以下は、これと同じような機能を提供するサーバである。 複数の接続先(クライアント)の要求を同時に処理するために、クライアント ごとに fork() システム・コールで専用のプロセスを作っている。

----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-server-fork.c -- 受け取った文字列をそのまま返すサーバ(fork版)
   4:	        /usr/local/LECTURES/syspro-1997-shinjo/ipc/echo-server-fork.c
   5:	        $Header: echo-server-fork.c,v 1.5 97/06/09 21:28:27 yas Exp $
   6:	        Start: 1997/06/09 19:46:40
   7:	*/
   8:	#include <stdio.h>
   9:	#include <sys/socket.h> /* socket() */
  10:	#include <netinet/in.h> /* struct sockaddr_in */
  11:	#include <netdb.h>      /* gethostbyname() */
  12:	#include <time.h>       /* gettimeofday(), struct timeval */
  13:	
  14:	main( argc,argv )
  15:	    int argc ;
  16:	    char *argv[] ;
  17:	{
  18:	    int portno ;
  19:	        if( argc >= 3 )
  20:	        {
  21:	            fprintf( stdout,"Usage: %s host port\n",argv[0] );
  22:	            exit( -1 );
  23:	        }
  24:	        if( argc == 2 )
  25:	            portno = atoi( argv[1] );
  26:	        else
  27:	            portno = getuid();
  28:	        echo_server( portno );
  29:	}
  30:	
  31:	echo_server( portno )
  32:	    int portno ;
  33:	{
  34:	    int acc,com ;
  35:	    pid_t child_pid ;
  36:	        acc = tcp_acc_port( portno );
  37:	        if( acc<0 )
  38:	            exit( -1 );
  39:	        print_host_port( portno );
  40:	        while( 1 )
  41:	        {
  42:	            if( (com = accept( acc,0,0 )) < 0 )
  43:	            {
  44:	                perror("accept");
  45:	                exit( -1 );
  46:	            }
  47:	            tcp_peeraddr_print( com );
  48:	            if( (child_pid=fork()) > 0 ) /* parent */
  49:	            {
  50:	                close( com );
  51:	            }
  52:	            else if( child_pid == 0 )
  53:	            {
  54:	                close( acc );
  55:	                echo_reply( com );
  56:	                printf("[%d,%d] connection closed.\n",getpid(),com );
  57:	                close( com );
  58:	                exit( 0 );
  59:	            }
  60:	        }
  61:	}
  62:	
  63:	#define BUFFSIZE        1024
  64:	
  65:	echo_reply( com )
  66:	    int com ;
  67:	{
  68:	    char buff[BUFFSIZE] ;
  69:	    int rcount ;
  70:	    int wcount ;
  71:	
  72:	        while( (rcount=read(com,buff,BUFFSIZE)) > 0 )
  73:	        {
  74:	            if( (wcount=writen(com,buff,rcount))!= rcount )
  75:	            {
  76:	                 perror("write");
  77:	                 exit( 1 );
  78:	            }
  79:	            printf("[%d,%d] ",getpid(),com );
  80:	            fflush( stdout );
  81:	            write( 1, buff, rcount );
  82:	        }
  83:	}
  84:	
  85:	tcp_peeraddr_print( com )
  86:	    int com ;
  87:	{
  88:	    struct sockaddr_in addr ;
  89:	    int addr_len ;
  90:	    union {
  91:	        int i ;
  92:	        unsigned char byte[4] ;
  93:	    } x ;
  94:	        addr_len = sizeof( addr );
  95:	        if( getpeername( com, &addr, &addr_len  )<0 )
  96:	        {
  97:	            perror("print_peeraddr");
  98:	        }
  99:	        x.i = addr.sin_addr.s_addr ;
 100:	        printf("[%d,%d] connection from %d.%d.%d.%d:%d\n",getpid(),com,
 101:	               x.byte[0],x.byte[1],x.byte[2],x.byte[3],
 102:	               ntohs( addr.sin_port ));
 103:	}
<以下省略>
----------------------------------------------------------------------

実行例。

サーバ側。サーバは、終了しないので、最後に、^CDel を押して、割り込みを掛けて終了させる。


----------------------------------------------------------------------
% ./echo-server-fork  [←]
echo orchid 1231 
[9930,4] connection from 130.158.86.225:1849
[9956,4] 012
[9930,4] connection from 127.0.0.1:1859
[9963,4] abc
[9963,4] def
[9963,4] connection closed.
[9956,4] 345
[9956,4] connection closed.
^C
% []
----------------------------------------------------------------------
クライアント側(その1)。
----------------------------------------------------------------------
% telnet orchid 1231 [←]
Trying...
Connected to orchid.
Escape character is '^]'.
012[←]
012
345[←]
345
^]
telnet> quit[←]
Connection closed.
% []
----------------------------------------------------------------------
クライアント側(その2)。localhost (127.0.0.1) は、自分自信を意味する。
----------------------------------------------------------------------
% telnet localhost 1231 [←]
Trying...
Connected to localhost.
Escape character is '^]'.
abc[←]
abc
def[←]
def
^]
telnet> quit[←]
Connection closed.
% []
----------------------------------------------------------------------

★練習問題(37) IPアドレスをホスト名に逆変換する

上の tcp_peeraddr_print() は、IP アドレスを数字で表示していた。 これを、ホスト名で表示するようにしなさい。 それには、gethostbyaddr()を利用するとよい。

普通は、ホスト名からIPアドレスを調べる手続き gethostbyname() が使われ る。gethostbyaddr() は、その逆を行う手続きである。

★練習問題(38) アクセス制御

ここで紹介しているサーバのプログラムは、どのホストからの要求も受け付け ている。特定のホストからの要求しか受け付けないように、変更しなさい。 たとえば、次のようなことを実現しなさい。

ヒント:上のプログラムで、tcp_peeraddr_print() は、接続相手のIPアドレ スとポート番号を表示している。同じようにな手続きを書き、表示ではなくて、 接続を許すか許さないかをチェックするようにするとよい。

■echo-server-select

echo-server-fork.c では、クライアントから接続要求を受け付ける たびに、新しいプロセスを作っていた。echo-server-select.cでは、 1つのプロセスで実行している。

----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-server-select.c -- 受け取った文字列をそのまま返すサーバ(select版)
   4:	        /usr/local/LECTURES/syspro-1997-shinjo/ipc/echo-server-select.c
   5:	        $Header: echo-server-select.c,v 1.1 97/06/09 21:47:47 yas Exp $
   6:	        Start: 1997/06/09 19:53:33
   7:	*/
   8:	#include <stdio.h>
   9:	#include <sys/socket.h> /* socket() */
  10:	#include <netinet/in.h> /* struct sockaddr_in */
  11:	#include <netdb.h>      /* gethostbyname() */
  12:	#include <time.h>       /* gettimeofday(), struct timeval */
  13:	#include <sys/types.h>  /* fd_set */
  14:	
  15:	main( argc,argv )
  16:	    int argc ;
  17:	    char *argv[] ;
  18:	{
  19:	    int portno ;
  20:	        if( argc >= 3 )
  21:	        {
  22:	            fprintf( stdout,"Usage: %s host port\n",argv[0] );
  23:	            exit( -1 );
  24:	        }
  25:	        if( argc == 2 )
  26:	            portno = atoi( argv[1] );
  27:	        else
  28:	            portno = getuid();
  29:	        echo_server( portno );
  30:	}
  31:	
  32:	echo_server( portno )
  33:	    int portno ;
  34:	{
  35:	    int acc,com ;
  36:	    fd_set readfds,readfds_save ;
  37:	    int i,n ;
  38:	
  39:	        acc = tcp_acc_port( portno );
  40:	        if( acc<0 )
  41:	            exit( -1 );
  42:	        print_host_port( portno );
  43:	
  44:	        FD_ZERO( &readfds_save );
  45:	        FD_SET( acc,&readfds_save );
  46:	        while( 1 )
  47:	        {
  48:	            readfds = readfds_save ;
  49:	            n = select( FD_SETSIZE,&readfds,0,0,0 );
  50:	            if( n <= 0 )
  51:	            {
  52:	                perror("select");
  53:	                exit( 1 );
  54:	            }
  55:	            if( FD_ISSET(acc,&readfds) )
  56:	            {
  57:	                FD_CLR( acc,&readfds );
  58:	                if( (com = accept( acc,0,0 )) < 0 )
  59:	                {
  60:	                    perror("accept");
  61:	                    exit( -1 );
  62:	                }
  63:	                FD_SET( com, &readfds_save );
  64:	                tcp_peeraddr_print( com );
  65:	            }
  66:	            for( i=0 ; i<FD_SETSIZE ; i++ )
  67:	            {
  68:	                if( FD_ISSET(i,&readfds) )
  69:	                {
  70:	                    if( echo_reply_once( i )<=0 )
  71:	                    {
  72:	                        printf("[%d,%d] connection closed.\n",getpid(),i );
  73:	                        close( i );
  74:	                        FD_CLR( i,&readfds_save );
  75:	                    }
  76:	                }
  77:	            }
  78:	        }
  79:	}
  80:	
  81:	#define BUFFSIZE        1024
  82:	
  83:	echo_reply_once( com )
  84:	    int com ;
  85:	{
  86:	    char buff[BUFFSIZE] ;
  87:	    int rcount ;
  88:	    int wcount ;
  89:	
  90:	        if( (rcount=read(com,buff,BUFFSIZE)) > 0 )
  91:	        {
  92:	            if( (wcount=writen(com,buff,rcount))!= rcount )
  93:	            {
  94:	                 perror("write");
  95:	                 exit( 1 );
  96:	            }
  97:	            printf("[%d,%d] ",getpid(),com );
  98:	            fflush( stdout );
  99:	            write( 1, buff, rcount );
 100:	        }
 101:	        return( rcount );
 102:	}
<以下省略>
----------------------------------------------------------------------

実行例。

サーバ側。サーバは、終了しないので、最後に、^CDel を押して、割り込みを掛けて終了させる。


----------------------------------------------------------------------
% ./echo-server-select [←]
echo orchid 1231 
[9792,4] connection from 127.0.0.1:1704
[9792,4] abc
[9792,5] connection from 130.158.86.225:1720
[9792,5] 012
[9792,5] 345
[9792,5] connection closed.
[9792,4] efg
[9792,4] connection closed.
^C
% []
----------------------------------------------------------------------
クライアント側(その1)。
----------------------------------------------------------------------
% telnet orchid 1231 [←]
Trying...
Connected to orchid.
Escape character is '^]'.
012
012
345
345
^]
telnet> quit[←]
Connection closed.
% []
----------------------------------------------------------------------
クライアント側(その2)。localhost (127.0.0.1) は、自分自信を意味する。
----------------------------------------------------------------------
% telnet localhost 1231 [←]
Trying...
Connected to localhost.
Escape character is ^]'.
abc[←]
abc
efg[←]
efg
^]
telnet> quit[←]
Connection closed.
% []
----------------------------------------------------------------------
'

★練習問題(39) fingerサーバ

finger サーバを作成しなさい。これは、受け取った文字列を引数にして、 finger コマンドを実行するとよい。そして、実行結果を、接続先に返すよう にするとよい。

finger コマンドと finger サーバの間は、パイプで接続しなさい。今までの 例題で利用した方法を組み合わせることで実現することができるはずである (pipe(),fork(),dup(),close(),execve()など)。popen() ライブラリ関数を 利用してもよい。

★練習問題(40) httpサーバ

http サーバを作成しなさい。これは、受け取ったファイル名のファイルを開 いて、内容を読み込み、それを接続先に返すようにするとよい。

この課題では、特にセキュリティに気をつけなさい。必ず次の条件を満たすよ うなサーバを作りなさい。

■tcp_wrapper

インターネットのセキュリティを高めるための道具として、TCP Wrapper があ る。TCP Wrapper は、クライアントの IP アドレスを調べて、接続を許可した り拒否したりする機能がある。そのような機能を持っていないプログラムに、 そのような機能を外付けで付け加えることができる。

次の場所にソースを置いておく。

/usr/local/LECTURES/syspro-1997-shinjo/tcp_wrappers_7.6/

■sized_io(initport())

TCP/IP のプログラムを書く時には、sized_io ライブラリを利用すると便利である。 stream.c は、短いので目を通しておくとよい。

次の場所にソースを置いておく。

/usr/local/LECTURES/syspro-1997-shinjo/sized_io/

★[report] report-9,TCP/IPのサーバ

練習問題(36),(39),(40) の中から1つ以上選んでやりなさい。問題を難しい 方に変更してもよい。 (注意:印刷資料には、(36) ではなくて、(37)になっていました。 )
[HTTP] [NNTP] [SMTP] [FINGER]
↑[もどる] ←[6月3日] ・[6月10日] →[6月17日]
Last updated: 1997/06/10 21:39:31
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>