最近の記事
- 9/1 - 将棋始めました
- 5/16 - サーバー引っ越し
- 4/24 - 優先順位
- 3/17 - vbNullString と 空文字列 ""
- 3/15 - InputBox 関数の戻り値
- 3/13 - 紙と Excel と VBA
- 3/9 - 未だに Visual Basic 6
- 11/14 - ぼて閉鎖
- 11/9 - 関数オブジェクトの呼び出し
- 9/7 - メソッドとしての関数オブジェクト
Entering Passive Mode
今日はもう一つの壁 UPnPNT を C++ で実装する。
面倒なので、関数化しておく。今のところ汎用じゃないけど。
void RegisterPortMapping(const sockaddr_in &localAddr, sockaddr_in *externalAddr) {
// 内部ホストの文字列 IP と、自然表現のポート番号を得る。
char *localIP = inet_ntoa(localAddr.sin_addr);
u_short localPort = ntohs(localAddr.sin_port);
// COM の初期化
CoInitialize(NULL);
そろそろメインの UPnPNT の実装をしたい所だが、
その前にまだまだ壁がある。今日はひとつの壁を取っ払おう。
FTP は行志向のプロトコルだ。コントロール接続においては、
クライアント・サーバともに行単位でデータをやり取りする。
コマンド行や応答行は、原則として CRLF で終端するので、
データを行単位で送受信できると効率が良い。
残念ながら、ソケットを使った TCP のプログラミングでは、
一度の send が、一度の recv で受け取られる保障はない。
TCP により送信される順番と到達は保障されるが、
それがどのように分割されるかは分からない。
今日はプロキシとしての実装を行う。
クライアントから接続があった場合、
代行してサーバに接続する。
auto_ptr<Socket> client(listener->Accept());
sockaddr_in sa;
memset(&addr, 0, sizeof(sockaddr_in));
sa.sin_family = AF_INET;
sa.sin_port = htons(21);
sa.sin_addr.s_addr = inet_addr("192.168.0.77");
やっと実装に入れる。
ネットワークプログラミングなので、
同じコンピュータでテストしても面白くない。
そこでプロキシ用にコンピュータを用意した。
まず必要なのが、クライアントの通信を待機するソケット。
ポートは 2121 にしよう。なお、IP は、192.168.0.111 だ。
sockaddr_in を準備。ここは普通の C コード
(クラス化してもいいんだけど……構想中。)
そして、ソケットのクラス。
class Socket {
public:
enum TransmissionDirection {
Incoming = 0x1,
Outgoing = 0x2,
Both = Incoming | Outgoing
};
Socket(int family, int type, int protocol);
explicit Socket(SOCKET socket);
前回もそうだけど、デストラクタなどメンバは一部省略。
// イベント
class Event : public WaitableHandle {
public:
Event(bool manualReset, bool initiallyActive) throw(WindowsException);
virtual void Activate(void) throw(WindowsException);
virtual void Inactivate(void) throw(WindowsException);
virtual void Pulse(void) throw(WindowsException);
};
いよいよ FTP サーバの実装に入る
プログラミング環境は色々と迷ったが、
オーソドックスに C/C++ にすることにした。
すぐにでもソケットの処理に入ってもいいが、
ここは趣味。最大限悪い癖を発揮して、
意味も無く外堀のクラス化を進めていこう。
ソケットを扱うサーバプログラミングの場合、
マルチスレッドになることが多い。
Thread 周りのクラスを作成しよう。
FTP サーバの構築は、ソケット部のプログラムだけなので楽だ。
問題になりそうなのは、NAT Traversal の実装。
UPnP の仕様を確認して実装するのは大変そうだ。
何か楽できないだろうか。調べてみた。
Windows XP 以降には、ICS/ICF という技術基盤がある。
一部として Network Address Translation Traversal があり、
API が公開されていることが分かった。
MSDN ライブラリで調べてみたが、どうやら C API ではなく、
インタフェースベースの API のようだ。
IUPnPNAT が親玉っぽいが、取得方法が分からない。
処理の流れを整理してみよう
大文字はグローバル、小文字はプライベート IP/Port
R: リモートホスト(クライアント)
P: FTP プロキシ IP/Port
S: FTP サーバ IP/Port
D: データ接続用 IP/Port
静: 静的 NAPT ルール
動: 動的 NAPT ルール
パケットをキャプチャして書き換えるのは、
ソフトウェアベースでは無理があるので、
FTP サーバプロキシを作成することにした。
もちろん、全部実装するような無駄なことはしたくないので、
基本的には全てそのまま、元の FTP サーバに転送する。
PASV を受けた場合のみ、ルータの持つ UPnP を利用して、
動的に NAPT テーブルを書き換えて、WAN 側 IP を返す。
C <=> P <=> S (クライアント、プロキシ、サーバ)