実行結果(telnet www.ie.u-ryukyu.ac.jp 80 -> GET /index.html HTTP/1.0)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=shift_jis"> <meta http-equiv="content-style-type" content="text/html"> <link rel="stylesheet" type="text/css" href="./css/stylesheet_1.css"> <link rel="SHORTCUT ICON" href="./icon/fav-ie2.ico"> ################################## </head> ################################### Sat Jan 1 12:53:12 JST 2005 --> <body bgcolor="#CC9966"> ######################### ########################## <div class="eng-logo"> <a href="http://www.ie.u-ryukyu.ac.jp/index-e.html" style="text-decoration:none; ">
変更点
tcpinetd 5682/tcp を追加
tcpinetd stream tcp nowait root //Users/konoikehiroki/Desktop/TCP_Programing/server を追加 #/sbin/service --list を /sbin/service --list と変更
#define HOST_NAME "hoge" を "nw0519.st.ie.u-ryukyu.ac.jp" と変更
#define DATAFILE "data" を "/Users/konoikehiroki/Desktop/TCP_Programing/data.txt" と変更
実行結果(client.c)
Connected. Input Keyword = warning: this program uses gets(), which is unsafe. yama Keyword = [yama] / Data = [kawa] Input Keyword = xyz Keyword = [xyz] / Data = [XYZ] Input Keyword = 123 Keyword = [123] / Data = [456] Input Keyword = xxxx Keyword = [xxxx] / Data = [yyyy] Input Keyword = shiro Keyword = [shiro] / Data = [kuro] Input Keyword = kawa Keyword = [kawa] / Data = [] Input Keyword = ^C
client.c中の関数の説明
Connect()関数では、以下の既存の関数を用いて、ソケットの生成、ソケットの接続等を行っている。 以下に各関数の具体的な役割を示す。
プロトコル proto を用いるサービスの名前 name にマッチする行を /etc/services から探し、 その行の内容を収めた servent 構造体を返す。 proto が NULL の場合はどのプロトコルにもマッチしない。
関数は与えられたホスト名 name に対応する構造体 hostent を返す。ここで name はホスト名、ドット区切りの IPv4 アドレス、コロン区切りの (ドット区切りでも大丈夫かもしれない) IPv6 アドレスのいずれかである。
ファイルディスクリプタ sockfd が参照しているソケットを serv_addr で指定されたアドレスに接続する。 addrlen 引き数は serv_addr の大きさを示す。
通信のための端点(endpoint)を作成し、ディスクリプター(descriptor)を返す。
ファイル・ディスクリプター (descriptor) をクローズする。 そのディスクリプターは、どのファイルも参照していない状態になり、再使用が可能になる。
文字列sの長さを計算する。このとき、終端文字`\0'は計算に含まれない。
buf で示されるバッファから最大 count バイトまでをファイル・ディスクリプタ fd によって参照されるファイルへと書き込む。
RecvDate()関数では、read()関数を用いてディスクリプタからデータを読み込むことにより、 サーバからデータを受信することになる。
ファイル・ディスクリプター (file descriptor) fd から最大 count バイトを buf で始まる バッファーへ読み込もうとする。count が 0 ならば、read() は 0 を返し、他に何も起きない。
server.c中の関数の説明
GetLineFromPeer()関数では、read()関数を用いて、クライアントからのデータを受け取ることになる。
dataファイルからクライアントから受け取ったデータとマッチするものがないか探し出し、 それに対応するデータを返す。このデータをmain()関数中でwrite()関数を用いてクライアントに送信する。
サーバ・クライアントプログラムの動作フロー
Source Code
inetdを使わない場合は、inetdを使う場合に比べて以下の関数を追加する必要がある。
inetdを用いたサーバプログラムでは,サーバはinetdに呼び出された時だけ 起動するので,メモリ等の資源を節約できる。しかし、通信の速度は呼び出されでから起動するため遅くなる。 デーモン型の方はサーバプログラムが常に起動しているため,通信の速度は inetdを利用する場合より早いがinetdの場合より, メモリ等の資源を消費してしまうという デメリットがある。
実行結果
[TCP_Programing : 12/12 15:21] ./noclient Input Keyword = yama Output Keyword = kawa Input Keyword = xyz Output Keyword = XYZ Input Keyword = 123 Output Keyword = 456 Input Keyword = xxxx Output Keyword = yyyy Input Keyword = ^C
Source Code
実行結果
[TCP_Programing : 12/12 15:31] ./gethtml Input server address = www.u-ryukyu.ac.jp Connected. download index.html (yes?/no?) yes HTTP/1.1 200 OK Date: Tue, 12 Dec 2006 06:33:01 GMT Server: Apache/2.2.2 (Fedora) Last-Modified: Tue, 12 Dec 2006 06:11:30 GMT ETag: "122b27d-23a6-2734a880" Accept-Ranges: bytes Content-Length: 9126 Connection: close Content-Type: text/html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> ####################################### <style type="text/css"> <!-- td { font-size: smaller; } --> </style> <!-- <script language="javascript" src="preview/rollover.js"></script> --> <style type="text/css"> </style> <link href="preview/ryudai2.css" rel="stylesheet" type="text/css"> </head>
Source Code
実行結果
[TCP_Programing : 12/12 15:35] ./portscan Input address : 133.13.59.19 Port number = 80 : use Port number = 139 : use Port number = 427 : use Port number = 445 : use Port number = 548 : use Port number = 631 : use Port Scan end Disocnnected.
問題の原因
操作していないバッファdmy[]に値が入ることがあるという問題の原因は, gets()関数に起因する。このgets()関数は読み込む文字列のサイズを 指定できないので,バッファのサイズを超えて読み込みを続けてしまうことがあり, このために、バッファのサイズを超える文字列を読み込み結果として,buf[]に 格納できなくなり,dmy[]に値が入ってしまうことになる。gccのコンパイラーでも warning: this program uses gets(), which is unsafe.と警告が出る。
問題の解決方法
この問題の解決方法は、既に考えられており、fgets()関数を用いることで 解決することができる。fgets()関数では保存先のメモリだけではなく、そのメモリの サイズも指定することができるので、gets()関数のようにバッファのサイズを超えて文字列 を読み込むことはなくなる。 以下にこのgets()をfgets()に書き換えたプログラムを示す。
Source Code
実行結果
[TCP_Programing : 12/12 16:06] ./bo_fg before buf(Len:0) = dmy(Len:0) = 1234567890 after buf(Len:2) = 12 dmy(Len:0) =
セキュリティ上の問題
バッファオーバーフローを悪用した不正アクセスを行なうには高度な知識が必要だが、 成功するとバッファオーバーフローを引き起こしたプログラムが持っているアクセス権 の範囲で任意の動作を行なうことが可能となる。 一般にWebサーバなど、インターネット経由でサービスを提供するプログラムは管理者権限で 稼動しているため、こういったプログラムのバッファオーバーフローを使われてしまうと、 不正侵入者に管理者権限を奪われることもある。