/*	
 *	cliptcp.c
 *
 *	TCPプロトコルドライバ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Tue Sep 20 04:34:00 JST 2005 Naoyuki Sawa
 *	- 1st リリース。
 *	* Fri Sep 23 18:31:00 JST 2005 Naoyuki Sawa
 *	- 不正な応答確認番号を伴うACKを受信した場合の処理を修正しました。
 */
#include "clip.h"

/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */
/* ★ 各状態の挙動設計については、tcp/tcp.xlsを参照してください。★ */
/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */

/****************************************************************************
 *	TCPプロトコルドライバ
 ****************************************************************************/

#define TCP_RECV_MSS	1460	/* 受信MSS (Ethernet環境の最大MSS) (調整可) */
#define TCP_SEND_MSS	 536	/* 送信MSS (保証されている最小MSS) (調整可) */

#define TIMER_INTERVAL	1000	/* 再送駆動用タイマインターバル[ms](調整可) */
#define RESEND_INTERVAL	2000	/* 再送インターバル[ms]            (調整可) */

#define CLOSE_TIMEOUT	1000	/* 切断待ちタイムアウトの既定値[ms](調整可) */

typedef struct _TCPDRIVER {
	IPCLIENT ip_client;	/* IPクライアント登録用 */
	int timer_id;		/* 再送駆動用タイマID */
	LIST_ENTRY client_list;	/* クライアントリスト */
} TCPDRIVER;
static TCPDRIVER tcp_driver;

static TCPCLIENT* tcp_find_client(int local_port, int remote_ip_address, int remote_port);
static int tcp_allocate_ephemeral_port();
static void tcp_send_segment(TCPCLIENT* client, int control_flag, const void* data, int data_length);
static void tcp_reply_rst(TCPHEADER* received_header, int remote_ip_address);
static void tcp_init_state(TCPCLIENT* client, int state);
static void tcp_exec_state(TCPCLIENT* client);
static void tcp_timer_callback(void* context);
static void tcp_recv_callback(IPCLIENT* ip_client, int source_ip_address, int destination_ip_address, const unsigned char* data/*[length]*/, int length);

/*--------------------------------------------------------------------------*/

/* クライアントリストから、指定したローカルポート、リモートIPアドレス、リモートポートに対応するクライアントを探します。
 * 指定したローカルポート、リモートIPアドレス、リモートポートに対応するクライアントが見つかったら、ポインタを返します。
 * 指定したローカルポート、リモートIPアドレス、リモートポートに対応するクライアントが見つからなければ、NULLを返します。
 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static TCPCLIENT*
tcp_find_client(int local_port, int remote_ip_address, int remote_port)
{
	LIST_ENTRY* list_head = &tcp_driver.client_list;	/* リストのルート要素 */
	LIST_ENTRY* list_entry = list_head->Flink;		/* 先頭の要素(or終端) */
	TCPCLIENT* client;

	while(list_entry != list_head) {
		if(!list_entry) {
			DIE(); /* 未初期化、または、クライアントリスト破損 */
		}
		client = CONTAINING_RECORD(list_entry, TCPCLIENT, list_entry);
		if((client->local_port        == local_port       ) &&
		   (client->remote_ip_address == remote_ip_address) &&
		   (client->remote_port       == remote_port      )) {
			return client;
		}
		list_entry = list_entry->Flink;
	}

	return NULL;
}

/* 未使用のエフェメラルポートを探します。
 * ------------------------------------------------------------------------------------------------------------------------------------------------------------
 * IT用語辞典 e-Words ： エフェメラルポートとは 【ephemeral ports】 - 意味・解説
 * 特定の用途(プロトコルでの使用が推奨されておらず、一時的な通信のために自由に利用できるポート。ダイナミックポートとも言われる。「ephemeral」は「短命な」の意。
 * これに対して、HTTPの80番のように、特定のプロトコルが利用することが広く推奨されているポートはウェルノウンポート(well-known port)と呼ばれる。
 * エフェメラルポートは主にクライアント側で利用される。クライアントからサーバにリクエストを送出する際、
 * サーバ側のポートは目的のサービスが稼動しているポート(多くの場合、ウェルノウンポート)である必要があるが、クライアント側はどんなポートを使ってももかまわない。
 * このとき、クライアント側ではOSが空いているエフェメラルポートの中から適当なものを選んで割り当てる。通信が終了するとそのポートは解放される。
 * エフェメラルポートは、基本的には1024番以降が用いられるが、OSにより異なる。
 * Windowsを始め多くのOSでは1024番から5000番をエフェメラルポートとして割り当てているが、例えば、FreeBSD 5.0では49152番から65535番を割り当てている。
 * ------------------------------------------------------------------------------------------------------------------------------------------------------------
 * 本ドライバは、FreeBSDに倣い、49152番から65535番($C000〜$FFFF)をエフェメラルポートとして割り当てることにします。
 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static int
tcp_allocate_ephemeral_port()
{
	LIST_ENTRY* list_head = &tcp_driver.client_list;	/* リストのルート要素 */
	LIST_ENTRY* list_entry = list_head->Flink;		/* 先頭の要素(or終端) */
	TCPCLIENT* client;
	int ephemeral_port;

	for(ephemeral_port = 0xc000; ephemeral_port <= 0xffff; ephemeral_port++) {
		while(list_entry != list_head) {
			if(!list_entry) {
				DIE(); /* 未初期化、または、クライアントリスト破損 */
			}
			client = CONTAINING_RECORD(list_entry, TCPCLIENT, list_entry);
			if(client->local_port == ephemeral_port) {
				break; /* 使用済み */
			}
			list_entry = list_entry->Flink;
		}
		if(list_entry == list_head) {
			break; /* 未使用ポート発見 */
		}
	}

	if(ephemeral_port > 0xffff) {
		DIE(); /* 未使用ポート無し */
	}

	return ephemeral_port;
}

/* tcp_send_segment()、tcp_reply_rst()から利用します。
 * 送信キューがいっぱいの場合、送信は失敗しますが、無視しても大丈夫です。
 * ネットワーク上でセグメントがロストしたのと同じと考えられ、再送処理にてカバーできるからです。
 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static void
_tcp_send_segment(TCPHEADER* header, int remote_ip_address)
{
	int packet_length;
	unsigned char* packet = NULL; /* 要解放 */
	//
	IPCONFIG ip_config;
	ip_get_config(&ip_config);

	/* TCPセグメント構築用バッファを確保します。 */
	packet_length = 20/*TCPヘッダ*/ + header->data_length/*データ*/;
	packet = malloc(packet_length);
	if(!packet) {
		DIE(); /* メモリ不足 */
	}

	/* TCPセグメントを構築します。 */
	packet_length = pack_tcp_header(
		header,			/* TCPヘッダ */
		packet,			/* TCPセグメント構築用バッファ */
		packet_length,		/* TCPセグメント構築用バッファの許容量 */
		ip_config.ip_address,	/* 送信元IPアドレス */
		remote_ip_address);	/* 宛て先IPアドレス */
	if(packet_length < 0) {
		DIE(); /* 必ず成功するはず */
	}

	/* TCPセグメントを送信します。 */
	ip_send(&tcp_driver.ip_client,	/* IPクライアント */
		remote_ip_address,	/* 宛て先IPアドレス */
		packet,			/* パケット */
		packet_length);		/* パケット長 */

	/* TCPセグメント構築用バッファを解放します。 */
	free(packet);
}

/* TCPセグメントを構築、送信し、再送判断の基準時刻をリセットします。 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static void
tcp_send_segment(TCPCLIENT* client, int control_flag, const void* data, int data_length)
{
	TCPHEADER header;

	if(!client->remote_ip_address || !client->remote_port) {
		DIE(); /* 未接続クライアントからの呼び出しは不可 */
	}

	/* データが有れば、PSHを追加します。(呼び出し元で指定する手間を省きます) */
	if(data_length) {
		control_flag |= TCP_PSH;
	}

	/* TCPヘッダを設定します。 */
	memset(&header, 0, sizeof(TCPHEADER));
	header.source_port		= client->local_port;			/* 送信元ポート */
	header.destination_port		= client->remote_port;			/* 宛て先ポート */
	header.sequence_number		= client->send_sequence_number;		/* シーケンス番号 */
	header.acknowledgement_number	= client->recv_sequence_number;		/* 応答確認番号 */
	header.control_flag		= control_flag;				/* コントロールフラグ */
	header.window			= buffer_space(client->recv_buffer);	/* ウインドウ */
	header.data			= (void*)data;				/* データ */
	header.data_length		= data_length;				/* データ長 */

	/* TCPセグメントを構築、送信します。 */
	_tcp_send_segment(&header, client->remote_ip_address);
}

/* 受信したTCPセグメントに対して、RSTを返送します。 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static void
tcp_reply_rst(TCPHEADER* received_header, int remote_ip_address)
{
	TCPHEADER header;

	/* TCPヘッダを設定します。 */
	memset(&header, 0, sizeof(TCPHEADER));
	header.source_port		= received_header->destination_port;	/* 送信元ポート */
	header.destination_port		= received_header->source_port;		/* 宛て先ポート */
	if(received_header->control_flag & TCP_ACK) {				/* シーケンス番号 */
		header.sequence_number	= received_header->acknowledgement_number;  /* シーケンス番号 */
	}
	header.control_flag		= TCP_RST;				/* コントロールフラグ */

	/* TCPセグメントを構築、送信します。 */
	_tcp_send_segment(&header, remote_ip_address);
}

/* 状態を格納し、送信処理を行います。 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static void
tcp_init_state(TCPCLIENT* client, int state)
{
	/* 状態を格納します。 */
	client->state = state;

	/* 送信処理を行います。 */
	tcp_exec_state(client);
}

/* 再送判断の基準時刻をリセットし、送信、または、再送処理を行います。 */
/* ★割り込み禁止状態で呼び出してください!!★ */
static void
tcp_exec_state(TCPCLIENT* client)
{
	const void* data;
	int data_length;

	/* 再送判断の基準時刻をリセットします。 */
	client->T0 = pceTimerGetCount();

	/* 状態によって... */
	switch(client->state) {

	case TCP_LISTEN:
	case TCP_CLOSED:
		/* 何もしません。 */
		break;

	case TCP_SYN_SENT:
		/* SYNを送信します。 */
		tcp_send_segment(client, TCP_SYN, NULL, 0);
		break;

	case TCP_SYN_RECEIVED:
		/* SYN･ACKを送信します。 */
		tcp_send_segment(client, TCP_SYN | TCP_ACK, NULL, 0);
		break;

	case TCP_ESTABLISHED:
	case TCP_CLOSE_WAIT:
		/* ACK、データを送信します。 */
		data = peek_buffer(client->send_buffer, &data_length);
		if(data_length > client->send_window) {
			data_length = client->send_window;
		}
		tcp_send_segment(client, TCP_ACK, data, data_length);
		break;

	case TCP_FIN_WAIT_1:
	case TCP_LAST_ACK:
		/* FIN･ACKを送信します。 */
		tcp_send_segment(client, TCP_FIN | TCP_ACK, NULL, 0);
		break;

	case TCP_FIN_WAIT_2:
	case TCP_CLOSING:
	case TCP_TIME_WAIT:
		/* ACKを送信します。 */
		tcp_send_segment(client, TCP_ACK, NULL, 0);
		break;

	default:
		DIE(); /* 不明な状態 */
	}
}

/* 再送インターバルを超えたクライアントの、再送処理を行います。 */
static void
tcp_timer_callback(void* context)
{
	LIST_ENTRY* list_head = &tcp_driver.client_list;	/* リストのルート要素 */
	LIST_ENTRY* list_entry = list_head->Flink;		/* 先頭の要素(or終端) */
	TCPCLIENT* client;
	int T1 = pceTimerGetCount();

	while(list_entry != list_head) {
		if(!list_entry) {
			DIE(); /* 未初期化、または、クライアントリスト破損 */
		}
		client = CONTAINING_RECORD(list_entry, TCPCLIENT, list_entry);
		if(T1 - client->T0 >= RESEND_INTERVAL) {
			tcp_exec_state(client);
		}
		list_entry = list_entry->Flink;
	}
}

static void
tcp_recv_callback(IPCLIENT* ip_client, int source_ip_address, int destination_ip_address, const unsigned char* data/*[length]*/, int length)
{
	int retval;
	TCPHEADER header;
	TCPCLIENT* client;

	/* TCPセグメントを展開し、ヘッダ情報を取得します。 */
	retval = unpack_tcp_header(&header, data, length, source_ip_address, destination_ip_address);
	if(retval < 0) {
		return; /* 不正セグメント */
	}

	/* 接続済み、または、相手アドレスとポートを指定して接続待ちのクライアントを検索します。 */
	client = tcp_find_client(header.destination_port, source_ip_address, header.source_port);
	if(!client) {
		/* 相手アドレスだけを指定して接続待ちのクライアントを検索します。 */
		client = tcp_find_client(header.destination_port, source_ip_address, 0);
		if(!client) {
			/* 相手ポートだけを指定して接続待ちのクライアントを検索します。 */
			client = tcp_find_client(header.destination_port, 0, header.source_port);
			if(!client) {
				/* 相手を指定しないで接続待ちのクライアントを検索します。 */
				client = tcp_find_client(header.destination_port, 0, 0);
				if(!client) {
					/* クライアントが無ければ、RSTを返送します。 */
					tcp_reply_rst(&header, source_ip_address);
					return; /* ここまで */
				}
			}
		}
	}

	/* RSTを受信したら、CLOSEDへ遷移し、処理を返します。
	 * ただし、LISTEN、CLOSEDならば、何もせずに処理を返します。
	 */
	if(header.control_flag & TCP_RST) {
		if((client->state != TCP_LISTEN) &&
		   (client->state != TCP_CLOSED)) {
			tcp_init_state(client, TCP_CLOSED);
		}
		return; /* いずれにせよ、RSTの処理はここまでです */
	}

	/* 正しい応答確認番号を伴うACKを受信したら、応答確認待ち送信データを破棄します。 */
	if(header.control_flag & TCP_ACK) {
		int ack_len = header.acknowledgement_number - client->send_sequence_number;
		int snd_len = client->send_buffer->size;
		if((ack_len >= 0) && (ack_len <= snd_len)) {
			read_buffer(client->send_buffer, NULL, ack_len);
			client->send_sequence_number += ack_len;
			client->send_window = header.window; /* 忘れないで!! */
		/* 不正な応答確認番号を伴うACKを受信したら、修復処理を行い、処理を返します。 */
		} else {
			/* * 未接続状態の一手順目で、不正な応答確認番号を伴うACKを受信した場合は、RSTを返送します。
			 *   その後、再送処理を行います。
			 * - 以前に、今回と同じIPアドレス、ポートの組で接続したあとに、
			 *   切断を行わずに終了したため、相手側のTCPがまだ生きていると考えられます。
			 *   当方は、新しい接続を要求しているのですから、まず、相手側のTCPを切断するのです。
			 *   相手側のTCPを切断後、再送処理にて、あらためて接続を行います。
			 *   (LISTENの再送処理は何もしないので、再送処理が意味を成すのはSYN-SENTの場合です。)
			 * - SYN-RECEIVEDも未接続状態のひとつですが、一手順目を通過したあとなので、RSTを返送しません。
			 *   実質、接続済み状態と同じと見なし、再送処理を行うだけで、修復できるはずです。
			 * * 接続済み状態で、不正な応答確認番号を伴うACKを受信した場合は、ただちに再送処理を行います。
			 * - 単純に、いくつかのパケットがロストしたか、送出に失敗していたと考えられます。
			 *   ロスト、または、送出に失敗したパケットを再送することで、修復できるはずです。
			 */
			if((client->state == TCP_LISTEN  ) ||
			   (client->state == TCP_SYN_SENT)) {
				tcp_reply_rst(&header, source_ip_address);
			}
			tcp_exec_state(client);
			return; /* ここまで */
		}
	}

	/* LISTEN、SYN-SENT、CLOSED以外ならば、シーケンス番号を検査します。
	 * シーケンス番号が不正ならば、再送処理を行い、処理を返します。
	 */
	if((client->state != TCP_LISTEN  ) &&
	   (client->state != TCP_SYN_SENT) &&
	   (client->state != TCP_CLOSED  )) {
		if(header.sequence_number != client->recv_sequence_number) {
			tcp_exec_state(client);
			return; /* ここまで */
		}
	}

	/* ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2ならば、データ受信を行います。 */
	if((client->state == TCP_ESTABLISHED) ||
	   (client->state == TCP_FIN_WAIT_1 ) ||
	   (client->state == TCP_FIN_WAIT_2 )) {
		/* 受信データがあれば... */
		if(header.data_length) {
			/* 受信バッファの空きがあれば... */
			if(header.data_length <= buffer_space(client->recv_buffer)) {
				/* 受信バッファに追加します。(必ず成功するはず) */
				write_buffer(client->recv_buffer, header.data, header.data_length);
				/* 受信シーケンス番号を更新します。 */
				client->recv_sequence_number += header.data_length;
				/* 応答確認を返送します。 */
				tcp_exec_state(client);
			}
		}
	}

	/* 状態によって... */
	switch(client->state) {

	case TCP_LISTEN:
		/* SYNを受信したら... */
		if(header.control_flag & TCP_SYN) {
			/* リモートIPアドレス、リモートポートを格納します。 */
			if(client->remote_ip_address) { /* 特定のリモートIPアドレスを待ち受けていた場合 */
				if(client->remote_ip_address != source_ip_address) {
					DIE(); /* 有り得ない */
				}
			} else {                        /* 任意のリモートIPアドレスを待ち受けていた場合 */
				client->remote_ip_address     = source_ip_address;
			}
			if(client->remote_port) {       /* 特定のリモートポートを待ち受けていた場合 */
				if(client->remote_port       != header.source_port) {
					DIE(); /* 有り得ない */
				}
			} else {                        /* 任意のリモートポートを待ち受けていた場合 */
				client->remote_port           = header.source_port;
			}
			/* 受信シーケンス番号、送信ウインドウを格納します。 */
			client->recv_sequence_number = header.sequence_number + 1/*SYN*/;
			client->send_window          = header.window;
			/* SYN-RECEIVEDへ遷移します。 */
			tcp_init_state(client, TCP_SYN_RECEIVED);
		}
		break;

	case TCP_SYN_SENT:
		/* SYN･ACKを受信したら... */
		if((header.control_flag & TCP_SYN) && (header.control_flag & TCP_ACK)) {
			/* 受信シーケンス番号、送信ウインドウを格納します。 */
			client->recv_sequence_number = header.sequence_number + 1/*SYN*/;
			client->send_window          = header.window;
			/* ESTABLISHEDへ遷移します。 */
			tcp_init_state(client, TCP_ESTABLISHED);
		}
		break;

	case TCP_SYN_RECEIVED:
		/* ACKを受信したら... */
		if(header.control_flag & TCP_ACK) {
			/* ESTABLISHEDへ遷移します。 */
			tcp_init_state(client, TCP_ESTABLISHED);
		}
		break;

	case TCP_ESTABLISHED:
		/* FINを受信したら... */
		if(header.control_flag & TCP_FIN) {
			/* 受信シーケンス番号を格納します。 */
			client->recv_sequence_number = header.sequence_number + 1/*FIN*/;
			/* CLOSE-WAITへ遷移します。 */
			tcp_init_state(client, TCP_CLOSE_WAIT);
		}
		break;

	case TCP_FIN_WAIT_1:
		/* FINを受信したら... */
		if(header.control_flag & TCP_FIN) {
			/* 受信シーケンス番号を格納します。 */
			client->recv_sequence_number = header.sequence_number + 1/*FIN*/;
			/* CLOSINGへ遷移します。 */
			tcp_init_state(client, TCP_CLOSING);
		/* FINを受信せずに、ACKを受信したら... */
		} else if(header.control_flag & TCP_ACK) {
			/* FIN-WAIT-2へ遷移します。 */
			tcp_init_state(client, TCP_FIN_WAIT_2);
		}
		break;

	case TCP_FIN_WAIT_2:
		/* FINを受信したら... */
		if(header.control_flag & TCP_FIN) {
			/* 受信シーケンス番号を格納します。 */
			client->recv_sequence_number = header.sequence_number + 1/*FIN*/;
			/* TIME-WAITへ遷移します。 */
			tcp_init_state(client, TCP_TIME_WAIT);
		}
		break;

	case TCP_CLOSING:
		/* ACKを受信したら... */
		if(header.control_flag & TCP_ACK) {
			/* TIME-WAITへ遷移します。 */
			tcp_init_state(client, TCP_TIME_WAIT);
		}
		break;

	case TCP_LAST_ACK:
		/* ACKを受信したら... */
		if(header.control_flag & TCP_ACK) {
			/* CLOSEDへ遷移します。 */
			tcp_init_state(client, TCP_CLOSED);
		}
		break;

	case TCP_CLOSE_WAIT:
	case TCP_TIME_WAIT:
	case TCP_CLOSED:
		/* 何もしません。 */
		break;

	default:
		DIE(); /* 不明な状態 */
	}
}

/*--------------------------------------------------------------------------*/

void
tcp_start(int txque_capacity)
{
	/* まず、確実に停止します。 */
	tcp_stop();

	/* ドライバ構造体をクリアします。 */
	memset(&tcp_driver, 0, sizeof tcp_driver);

	/* クライアントリストを初期化します。 */
	InitializeListHead(&tcp_driver.client_list);

	/* TCPプロトコルドライバを開始します。 */
	tcp_driver.ip_client.protocol = 6/*TCP*/;
	tcp_driver.ip_client.recv = tcp_recv_callback;
	ip_open(&tcp_driver.ip_client);

	/* 再送駆動用タイマを開始します。 */
	tcp_driver.timer_id = timer_start(TIMER_INTERVAL, tcp_timer_callback, NULL);
}

void
tcp_stop()
{
	/* 開始していなければ、何もしません。 */
	if(!tcp_driver.client_list.Flink) {
		return;
	}

	/* 再送駆動用タイマを停止します。 */
	timer_stop(tcp_driver.timer_id);

	/* TCPプロトコルドライバを停止します。 */
	ip_close(&tcp_driver.ip_client);

	/* ドライバ構造体をクリアします。 */
	memset(&tcp_driver, 0, sizeof tcp_driver);
}

void
tcp_open(TCPCLIENT* client, int active)
{
ENTER_CS;

	/* クライアントリストに追加します。 */
	InsertTailList(&tcp_driver.client_list, &client->list_entry);

	/* ローカルポートが指定されていなければ、エフェメラルポートを確保します。 */
	if(!client->local_port) {
		client->local_port = tcp_allocate_ephemeral_port();
	}

	/* 受信シーケンス番号、送信シーケンス番号、送信ウインドウを初期化します。 */
	client->recv_sequence_number = 0;
	client->send_sequence_number = pceTimerGetCount();
	client->send_window = 0;

	/* 送受信バッファを作成します。 */
	client->recv_buffer = create_buffer(TCP_RECV_MSS);
	client->send_buffer = create_buffer(TCP_SEND_MSS);

	/* SYNに対応するダミーの送信データを1バイト設定しておきます。
	 * この送信データは破棄されるのみで、実際には送信されません。
	 */
	client->send_buffer->size = 1;

	/* state,T0は、ActiveOpen、または、PassiveOpenの処理にて設定します。 */

	/* ActiveOpen */
	if(active) {
		if(!client->remote_ip_address || !client->remote_port) {
			DIE(); /* リモートIPアドレス、リモートポート指定必須 */
		}
		/* SYN-SENTへ遷移します。 */
		tcp_init_state(client, TCP_SYN_SENT);

	/* PassiveOpen */
	} else {
		/* LISTENへ遷移します。 */
		tcp_init_state(client, TCP_LISTEN);
	}

LEAVE_CS;
}

void
tcp_close(TCPCLIENT* client, int timeout)
{
	int T0 = pceTimerGetCount();

	/* 負数のタイムアウトが指定されたら、既定値とします。 */
	if(timeout < 0) {
		timeout = CLOSE_TIMEOUT;
	}

	do {

ENTER_CS;

		/* 状態によって... */
		switch(client->state) {

		case TCP_LISTEN:
		case TCP_SYN_SENT:
			/* CLOSEDへ遷移します。 */
			tcp_init_state(client, TCP_CLOSED);
			break;

		case TCP_SYN_RECEIVED:
			/* まだ、SYNに対応するダミーの送信データ1バイトが残っているはずです。
			 * FINに対応するダミーの送信データ1バイトとして、そのまま利用します。
			 */
			if(client->send_buffer->size != 1) {
				DIE(); /* 有り得ない */
			}
			/* FIN-WAIT-1へ遷移します。 */
			tcp_init_state(client, TCP_FIN_WAIT_1);
			break;

		case TCP_ESTABLISHED:
			/* 送信バッファが空になるのを待って... */
			if(!client->send_buffer->size) {
				/* FINに対応するダミーの送信データを1バイト設定しておきます。
				 * この送信データは破棄されるのみで、実際には送信されません。
				 */
				client->send_buffer->size = 1;
				/* FIN-WAIT-1へ遷移します。 */
				tcp_init_state(client, TCP_FIN_WAIT_1);
			}
			break;

		case TCP_CLOSE_WAIT:
			/* 送信バッファが空になるのを待って... */
			if(!client->send_buffer->size) {
				/* FINに対応するダミーの送信データを1バイト設定しておきます。
				 * この送信データは破棄されるのみで、実際には送信されません。
				 */
				client->send_buffer->size = 1;
				/* LAST_ACKへ遷移します。 */
				tcp_init_state(client, TCP_LAST_ACK);
			}
			break;
		}

		/* CLOSEDになるか、または、タイムアウトしたら...
		 * * 2005/09/19現在の実装では、TIME-WAITからの状態遷移を行いません。
		 *   本当は、TIME-WAITから短時間のタイムアウトでCLOSEDへ遷移するのですが、現在の実装はそうなっていません。
		 *   他の状態と同じく、tcp_close()に指定されたタイムアウトが経過するまで、TIME-WAITに留まってしまいます。
		 *   すなわち、当方から切断を開始した場合、必ずタイムアウトを待つことになり、プログラムの反応が遅れます。
		 * - この問題を回避するには、TIME-WAITをCLOSEDと同じ扱いとし、TIME-WAITになったら完了するという方法です。
		 *   TIME-WAITで短時間留まるべき理由は、最後のACKが相手に到達するのを待つという弱い理由によるものですが、
		 *   いずれにせよ相手からの応答は無く、確実に到達することを保証できるものでは有りません。
		 *   従って、TIME-WAITで短時間留まる必要はほとんどありません。
		 * - しばらく様子を見て、切断時のプログラムの遅れが気になるようならば、上述の方法で対策してください。
		 * →{{2005/09/19 上述の方法で対策しました}}
		 */
		//if((client->state == TCP_CLOSED) || (pceTimerGetCount() - T0 >= timeout)) {
		//↓{{2005/09/19 上述の方法で対策しました}}
		if((client->state == TCP_CLOSED) || (client->state == TCP_TIME_WAIT) || (pceTimerGetCount() - T0 >= timeout)) {

			/* クライアントリストから取り除きます。 */
			RemoveEntryList(&client->list_entry);

			/* 送受信バッファを解放します。 */
			delete_buffer(client->recv_buffer);
			delete_buffer(client->send_buffer);

			/* 切断待ちループを終了します。 */
			client = NULL;
		}

LEAVE_CS;

	} while(client);
}

int
tcp_recv(TCPCLIENT* client, void* data/*[maxlength]*/, int maxlength)
{
	int result;

ENTER_CS;

	/* 受信バッファからデータを読み出します。 */
	result = read_buffer(client->recv_buffer, data, maxlength);

	/* 受信データが有ったら... */
	if(result) {

		/* 受信バッファからデータを読み出したことにより、受信バッファが空になったら、
		 * 受信ウインドウが増加したことを通知するために、再送処理を行います。
		 * - “受信バッファが空になったら”という条件を含めないと、tcp_recv()が1バイトづつ要求された場合に、
		 *   毎回、受信ウインドウ増加の通知を送信することになり、送信バッファが溢れる可能性が高くなります。
		 *   このような問題を防ぐために、受信バッファが空になるまで、通知の送信を遅らせることにしました。
		 * - “受信バッファが空でない＝アプリケーションは別の処理を行っていて、受信データを待っていない”
		 *   と見なせるので、デッドロックにはならないはずです。
		 * - 受信可能状態以外でのこの処理は無意味ですが、行っても問題無いと思います。
		 */
		if(!client->recv_buffer->size) {
			tcp_exec_state(client);
		}

	/* 受信データが無ければ... */
	} else {
		switch(client->state) {

		/* 以下の状態は、受信可能を表します。
		 * このあとデータが到着する可能性があるので、“受信データ無し”として扱います。
		 */
		case TCP_ESTABLISHED:
		case TCP_FIN_WAIT_1:
			/* 既にresult=0です */
			break;

		/* 以下の状態は、接続待ちを表します。
		 * このあと受信可能となる可能性が有るので、“受信データ無し”と同じ扱いとします。
		 */
		case TCP_LISTEN:
		case TCP_SYN_SENT:
		case TCP_SYN_RECEIVED:
			/* 既にresult=0です */
			break;

		/* それ以外の状態は、受信終了を表します。
		 * このあと受信可能となる可能性は無いので、“受信終了”として扱います。
		 */
		default:
			result = -1;
			break;
		}
	}

LEAVE_CS;

	return result;
}

int
tcp_send(TCPCLIENT* client, const void* data/*[length]*/, int length)
{
	int result;

ENTER_CS;

	switch(client->state) {

	/* 以下の状態は、送信可能を表します。
	 * 送信バッファにデータを追加し、追加できたら、ACK、データを送信します。
	 */
	case TCP_ESTABLISHED:
	case TCP_CLOSE_WAIT:
		result = write_buffer(client->send_buffer, data, length);
		if(result > 0) {
			tcp_exec_state(client);
		}
		break;

	/* 以下の状態は、接続待ちを表します。
	 * このあと送信可能となる可能性が有るので、“送信バッファ空き無し”と同じ扱いとします。
	 */
	case TCP_LISTEN:
	case TCP_SYN_SENT:
	case TCP_SYN_RECEIVED:
		result = 0;
		break;

	/* それ以外の状態は、送信終了を表します。
	 * このあと送信可能となる可能性は無いので、“送信終了”として扱います。
	 */
	default:
		result = -1;
		break;
	}

LEAVE_CS;

	return result;
}

/****************************************************************************
 *	TCPソケット
 ****************************************************************************/

TCPSOCKET*
tcp_socket_open(int local_port, int remote_ip_address, int remote_port, int active)
{
	TCPSOCKET* tcp_socket;

	/* TCPソケット構造体のメモリを確保します。 */
	tcp_socket = malloc(sizeof(TCPSOCKET));
	if(!tcp_socket) {
		DIE(); /* メモリ不足 */
	}
	memset(tcp_socket, 0, sizeof(TCPSOCKET));

	/* TCPクライアントを登録します。 */
	tcp_socket->tcp_client.local_port        = local_port;
	tcp_socket->tcp_client.remote_ip_address = remote_ip_address;
	tcp_socket->tcp_client.remote_port       = remote_port;
	tcp_open(&tcp_socket->tcp_client, active);

	return tcp_socket;
}

void
tcp_socket_close(TCPSOCKET* tcp_socket, int timeout)
{
	/* TCPクライアントを登録解除します。 */
	tcp_close(&tcp_socket->tcp_client, timeout);

	/* TCPソケット構造体のメモリを解放します。 */
	free(tcp_socket);
}

int
tcp_socket_recv(TCPSOCKET* tcp_socket, void* data/*[maxlength]*/, int maxlength)
{
	return tcp_recv(&tcp_socket->tcp_client, data, maxlength);
}

int
tcp_socket_send(TCPSOCKET* tcp_socket, const void* data/*[length]*/, int length)
{
	return tcp_send(&tcp_socket->tcp_client, data, length);
}
