ネットdeダビングのクライアントソフトを作る(その1)

 RD-XV81を買ってVHSテープをダビングしたタイトルをPCにネットdeダビングしようと当時使っていたソフトを試そうとしたら既に消えているものや、64bit Windows環境で動かないもの、RD-XV81で動かないもの多数だったので面倒になって自分で作ってみた。
 ネットdeダビングは、ダビング元がNetBIOS over TCP/IPでダビング先のIPアドレスを取得して、ダビング元がダビング先のIPアドレスFTPサーバに接続、ダビング元のタイトル情報ファイルを送信、ダビング先の空き容量情報ファイルを受信、ダビング元のタイトルファイルを送信する仕組みになっている。
 ただし、通常のNetBIOS over TCP/IPと違い、コンピュータ名とは異なるGR-52010889AF25(グループ名とグループパスワードがTOSHIBAの場合)のNetBIOS名に対してダビング先のコンピュータ名で応答、トランザクションIDの上位8ビットはダビング先のHDDやDVDを表すので1(HDD)等に置き換え、ダビング先のFTPサーバのIPアドレスを含めて応答する必要がある。
 また、FTPサーバにログインする際のユーザー名とパスワードは時間によって毎回異なり、そのアルゴリズムも判らないので、どんなユーザー名でもログインできるようにAnonymous FTPサーバにしておく必要がある。

自分のIPアドレスを取得する

 ダビング元にIPアドレスを伝える必要があるので、予め自分のIPアドレスを取得する。UDPでダミーのIPアドレス(128.0.0.0等)とポート番号(7等)に接続すると、パケットは送信されないが、getsockname関数で自分のIPアドレスが取得できる。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[]) {
	int fd;
	struct sockaddr_in addr;
	socklen_t addrlen;
	unsigned long aa;
	char ad0,ad1,ad2,ad3;

	fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

	addr.sin_family=AF_INET;
	addr.sin_port=htons(7);
	inet_pton(AF_INET,"128.0.0.0",&addr.sin_addr.s_addr);
	connect(fd,(struct sockaddr *)&addr,sizeof(addr));

	addrlen=sizeof(addr);
	getsockname(fd,(struct sockaddr *)&addr,&addrlen);

	aa=ntohl(addr.sin_addr.s_addr);
	ad0=(aa&0xff000000)>>24;
	ad1=(aa&0x00ff0000)>>16;
	ad2=(aa&0x0000ff00)>>8;
	ad3=aa&0x000000ff;
	printf("%d.%d.%d.%d\n",ad0,ad1,ad2,ad3);

	close(fd);

	return 0;
}

UDPの137番ポートで受信したNetBIOS名を表示する

 UDPの137番ポートで受信した内容からNetBIOS名(15文字)とリソースタイプ(NetBIOS名に続く1バイト)をデコードして表示する。(終了させるにはCtrl+\を押す)

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[]) {
	int fd,rs,i;
	struct sockaddr_in addr;
	socklen_t addrlen;
	char buf[1024],name[16],rt;

	fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

	addr.sin_family=AF_INET;
	addr.sin_port=htons(137);
	addr.sin_addr.s_addr=htonl(INADDR_ANY);
	bind(fd,(struct sockaddr *)&addr,sizeof(addr));

	while(1) {
		addrlen=sizeof(addr);
		rs=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&addr,&addrlen);

		for(i=0;i<16;i++) name[i]=(buf[13+i*2]-0x41)*16+(buf[13+i*2+1]-0x41);
		rt=name[15];
		name[15]='\0';
		printf("%d %s<%02X>\n",rs,name,rt);
	}
	close(fd);

	return 0;
}

 C言語UDPTCPの送受信を行うためにソケットを使っている。Cコンパイラの環境によってはコンパイル時に-lsocketオプションが必要かも知れない。Windowsの場合はWinsockを使うように書き換えないといけないかも知れない。
 ソケットはファイルの読み書きと同じようにopen、read、write、closeのような手順で扱う。まずはsocketでプロトコル等を指定してソケットを作成、bindでソケットに名前をつける(アドレスを割り当てる)、recvfromでソケットから受信する、closeでソケットを閉じる。
 面倒なのはbindでのアドレスの割り当てで、sockaddr_in構造体にIPアドレスやポート番号をhtons関数やhtonl関数でビッグエンディアンに変換して格納した後、bind関数ではsockaddr_in構造体をsockaddr構造体として引き渡す必要があるのと、sockaddr_in構造体の大きさをsocklen_t型で引き渡す必要があること。
 ちなみにsocket関数などを使うためにBSD系の実装も想定するとも必要)が、close関数を使うためにが、UDPプロトコルを表すIPPROTO_UDPなどを使うためにが、htons関数などを使うためにをincludeしておく必要がある。
 尚、AF_INETはIPv4、SOCK_DGRAMはデータグラム(実質的にUDPプロトコルで使う)を表している。また、INADDR_ANYは任意のIPアドレス(全てのIPアドレスからの受信を行う場合など)を表している。
 NetBIOS over TCP/IPは0~1バイト目がトランザクションID(16ビット値)、13~44バイト目の32バイトは偶数バイトが上位4ビットに0x41を加えたもの、奇数バイトが下位4ビットに0x41を加えたものとなっており、これがNetBIOS名の15バイトとリソースタイプの1バイトに変換できる。