NetBIOSの名前問い合わせに返答する

test126.JPG
TOSHIBAのHDDレコーダー「RD-H1」などは「ネットdeダビング」という機能によりLAN経由による映像データのコピー(ダビング)に対応している。

この「ネットdeダビング」ではダビング先を探す際にNetBIOSの名前解決を利用している。ここではUDPのポート137を使ったNetBIOSの名前問い合わせに対してRDシリーズ向けの返答を返している。ここで返す応答はNetBIOSに則っているようで反したものになっている。そのため実際のNetBIOS機器に流用する際は注意が必要だ。

#include <mstcpip.h>
#include <winsock2.h>
#pragma	comment(lib,"Ws2_32.lib")



//////////////////////////////////////////////////////
//以下の構造体はWindows SDKのサンプルに含まれる「iphdr.h」内からコピー
//

#pragma	pack(push,1)

//
// IPv4 Header (without any IP options)
//
typedef struct ip_hdr
{
    unsigned char  ip_verlen;        // 4-bit IPv4 version
                                     // 4-bit header length (in 32-bit words)
    unsigned char  ip_tos;           // IP type of service
    unsigned short ip_totallength;   // Total length
    unsigned short ip_id;            // Unique identifier 
    unsigned short ip_offset;        // Fragment offset field
    unsigned char  ip_ttl;           // Time to live
    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)
    unsigned short ip_checksum;      // IP checksum
    unsigned int   ip_srcaddr;       // Source address
    unsigned int   ip_destaddr;      // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

//
// Define the UDP header 
//
typedef struct udp_hdr
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned short udp_length;       // Udp packet length
    unsigned short udp_checksum;     // Udp checksum
} UDP_HDR, *PUDP_HDR;

#pragma	pack(pop)


//////////////////////////////////////////////////////

#pragma	pack(push,1)

typedef	struct ip_hdr_udp
{
	IPV4_HDR	sHdrIp;
	UDP_HDR		sHdrUdp;
	BYTE		pData[1];

} UDP_IP_HDR;



typedef	struct netbios_name_reply
{
	USHORT	nTransactionID;
	USHORT	nFlags1;
	USHORT	nQuestion;
	USHORT	nAnswer;
	USHORT	nAuthority;
	USHORT	nAdditional;
	char	cbX;
	char	pszName[32];
	char	cbY;
	USHORT	nType;
	USHORT	nClass;
	USHORT	nTTL1;
	USHORT	nTTL2;
	USHORT	nLen;
	USHORT	nFlags2;
	UINT	nSrcAddr;
} NETBIOS_NAME_REPLY;


#pragma	pack(pop)


enum	RD_DEVICE_ID
{
	DEVICE_HDD			= 1,
	DEVICE_DVD			= 2,
	DEVICE_HDD_DVD		= 3,
	DEVICE_HDD2			= 4,
	DEVICE_HDD_HDD2		= 5,
	DEVICE_DVD_HDD2		= 6,
	DEVICE_HDD_DVD_HDD2	= 7,
	DEVICE_4			= 8,
	DEVICE_HDD_4		= 9,
	DEVICE_DVD_4		= 10,
	DEVICE_HDD_DVD_4	= 11,
	DEVICE_HDD2_4		= 12,
	DEVICE_HDD_HDD2_4	= 13,
	DEVICE_DVD_HDD2_4	= 14,
	//DEVICE_HDD_DVD_HDD2	= 15,
	DEVICE_5			= 16,
	DEVICE_HDD_5		= 17,
};




//
//	TOSHIBA RDシリーズ向けの名前問い合わせ返答処理
//
//【目的】
//TOSHIBA RDシリーズではネットDEダビングなどのためにダビング先機器を検索する際に
//UDP/137(NetBIOSの名前問い合わせ)を送信する。
//ここではその問い合わせに対して返答を返す。
//
//【処理の流れ】
//WinSock2のRAWソケットで全パケットをキャプチャする。
//キャプチャデータのうちUDP/137(NetBIOS名前問い合わせ)以外は無視
//NetBIOS名前問い合わせに対する返答をTOSHIBAのRDシリーズ用に送信
//
bool	ReplyNetBiosNS()
{
	int		nRet;
	SOCKET	sock;
	DWORD	dwReturned;

	//RAW SOCKETを作る
	sock = ::WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sock == INVALID_SOCKET)
		return false;

	{
		SOCKADDR_IN	sSockAddrIn;

		//パケットを監視するインターフェースを選ぶ
		//ここでは簡易のため最初に見つかったインターフェースを利用している
		//PCに複数のインターフェース(無線LANと有線LANなど)がある場合は適宜変更すること
		//無線LANが何番目に見つかるかなど、物理的なNICとの関連付けをしたい場合は、GetIfTableを利用する
		{
			int		nNo;
			BYTE	pBuff[1024];
			SOCKET_ADDRESS_LIST* pSockAddrList;

			//何番目に見つかったインターフェースを利用するか
			//2番目のインターフェースを利用したい場合は「1」に変更
			nNo = 0;

			// アダプタの列挙
			nRet = ::WSAIoctl(sock,SIO_ADDRESS_LIST_QUERY,NULL,0,pBuff,1024,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR || dwReturned == 0)
			{
				::closesocket(sock);
				return false;
			}

			pSockAddrList = (SOCKET_ADDRESS_LIST*)pBuff;

			if(nNo >= pSockAddrList->iAddressCount)
			{
				::closesocket(sock);
				return false;
			}
			sSockAddrIn.sin_addr.s_addr= ((SOCKADDR_IN*)pSockAddrList->Address[nNo].lpSockaddr)->sin_addr.s_addr;
		}


		//バインドとキャプチャ-の有効化
		//WSAIoctlに渡すコマンドによってキャプチャ-種などを変更できる
		{
			BOOL	bOption;

			//sSockAddrIn.sin_addr.s_addr	= INADDR_ANY; <-これはNG.必ずSIO_ADDRESS_LIST_QUERYで求めた値を利用する
			sSockAddrIn.sin_family	= AF_INET;
			sSockAddrIn.sin_port	= htons(0);

			nRet = ::bind(sock,(sockaddr*)&sSockAddrIn,sizeof(SOCKADDR_IN));
			if(nRet == SOCKET_ERROR) 
			{
				::closesocket(sock);
				return false;
			}

			//パケットキャプチャの有効化(Windows 2000以降対応)
			bOption = TRUE;
			nRet = ::WSAIoctl(sock,SIO_RCVALL,&bOption,sizeof(BOOL),NULL,0,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR)
			{
				::closesocket(sock);
				return false;
			}
		}
	}





	//キャプチャの実行とNetBIOS名前問い合わせへの返答
	{
		char	pBuff[204800];
		WSABUF	sWsaBuf;
		DWORD	dwFlags;
		DWORD	dwTick;

		dwTick = ::GetTickCount();

		while(1)
		{
			::Sleep(0);

			//処理時間を10秒間に制限
			if(::GetTickCount() - dwTick > 10*1000)
				break;


			dwFlags = 0;
			::ZeroMemory(pBuff,sizeof(pBuff));
			sWsaBuf.buf = pBuff;
			sWsaBuf.len = sizeof(pBuff);

			//パケット受信
			nRet = ::WSARecv(sock,&sWsaBuf,1,&dwReturned,&dwFlags,NULL,NULL);
			if(nRet == SOCKET_ERROR)
				continue;


			//受信データの解析
			{
				UDP_IP_HDR*	pHdr;

				//UDP分のデータ長がなければ処理しない
				if(dwReturned < sizeof(UDP_IP_HDR))
					continue;

				pHdr = (UDP_IP_HDR*)pBuff;

				//IPv4、かつ、IPヘッダーサイズが5×32bit以外のときは処理しない
				if(pHdr->sHdrIp.ip_verlen != 0x45)
					continue;

				//UDP/139以外は処理しない
				if(pHdr->sHdrIp.ip_protocol != IPPROTO_UDP || pHdr->sHdrUdp.dest_portno != ::htons(137))
					continue;


				//NetBIOSの名前問い合わせに返答する。。。ただしTOSHIBAのHDDレコーダーRDシリーズ形式で返す
				{
					UINT	i;
					BYTE	cbDeviceID;
					BYTE	cbTransactionID;
					NETBIOS_NAME_REPLY	sReply;
					char	pszName[] = "usefullcode.net";	//機器名、最大15文字

					//TOSHIBA RDシリーズではトランザクションIDの1バイト目がデバイス種別になっている
					cbDeviceID		= DEVICE_HDD;			//enum RD_DEVICE_ID内の各値を使用可能
					cbTransactionID = pHdr->pData[1];

					::ZeroMemory(&sReply,sizeof(NETBIOS_NAME_REPLY));
					sReply.nTransactionID = ::htons((cbDeviceID << 8) + cbTransactionID);
					sReply.nFlags1		= ::htons(0x8500);
					sReply.nQuestion	= 0;
					sReply.nAnswer		= ::htons(0x0001);
					sReply.cbX			= 0x32;

					//NetBIOS形式で名前をエンコード
					for(i = 0;i < 15; i++)
					{
						if(i < ::strlen(pszName))
						{
							sReply.pszName[i*2+0] = ((pszName[i] & 0xF0) >> 4) + 'A';
							sReply.pszName[i*2+1] = ((pszName[i] & 0x0F) >> 0) + 'A';
						}
						else
						{
							sReply.pszName[i*2+0] = ((' ' & 0xF0) >> 4) + 'A';
							sReply.pszName[i*2+1] = ((' ' & 0x0F) >> 0) + 'A';
						}
					}

					sReply.nType		= ::htons(0x0020);
					sReply.nClass		= ::htons(0x0001);
					sReply.nLen			= 0;
					sReply.nSrcAddr		= pHdr->sHdrIp.ip_srcaddr;


					SOCKET		sock;
					sockaddr_in	addr;

					sock = ::socket(AF_INET,SOCK_DGRAM,0);

					addr.sin_family		= AF_INET;
					addr.sin_port		= ::htons(137);
					addr.sin_addr.s_addr= pHdr->sHdrIp.ip_srcaddr;

					//返答の送信
					::sendto(sock,(char*)&sReply,sizeof(NETBIOS_NAME_REPLY),0,(sockaddr*)&addr,sizeof(sockaddr));

					::closesocket(sock);
				}
			}
		}
	} 

	::closesocket(sock);

	return	true;
}


bool	Test()
{
	WSAData wsaData;

	//Winsock2.2
	::WSAStartup(MAKEWORD(2,2),&wsaData);

	ReplyNetBiosNS();

	::WSACleanup();

	return	true;
}

プロジェクトファイルをダウンロード


カテゴリー「ネットワーク」 のエントリー