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;
}
