【C++】STUNサーバーにグローバルIPアドレスを問い合わせる

みんなが作った便利な関数やサンプルを共有するコミュニティです。
[url]http://www.activebasic.com/forum/viewforum.php?f=2]ActiveBasicの「実践コードモジュール」[/url]的な感じでやりましょう。
フォーラム(掲示板)ルール
・投稿するコードはできるだけ一つ、もしくは一つの関数を補助する複数の関数の形式にするか、
それだけをコンパイルして動くソースコード一式の形にしてください。
記事には説明だけを書き、コードは添付ファイルにしてもかまいません。
・使い方などの説明も書いてください。
環境に依存するコードの場合は、対象の環境も書いてください。
・使用条件(ライセンスなど)も書いていただけるとありがたいです。
・C言語、もしくはC++推奨ですが、他の言語でもかまいません。
・コードは正しくcodeタグで囲みましょう。
・一つのスレッドで一つのサンプルが基本です。
関連するサンプルの場合はまとめてもかまいません。
・投稿したサンプルを修正する場合には、スレッドの返信の形で投稿してください。
(新しいスレッドにしないでください。記事の編集でもかまいません)
返信
アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

【C++】STUNサーバーにグローバルIPアドレスを問い合わせる

#1

投稿記事 by みけCAT » 9年前

チャットで存在を教えてもらったので、コードの形にしてメモ。
STUNは本来はNATの操作を知るためのプロトコルらしいですが、
難しいことは一旦無視して、とりあえずグローバルIPアドレスを知るために利用してみます。
RFC3489の11. Protocol Detailsを参考にしました。

C++要素はstd::stringとthrow/try/catchくらいしかないので、ちょっと頑張ればC言語にできると思います。
Winsockを使用しています。Linuxなどの環境では適宜移植してください。

STUNを利用してグローバルIPアドレスを知る方法
1. STUNサーバーにBinding Requestを投げる。Attributesはなし、Message LengthもTransaction IDも全て0x00で良い
2. STUNサーバーからBinding Responseが返ってくるはずなので、受信する。
3. メッセージの中からMAPPED-ADDRESS(必須なのであるはず)を探す。
4. MAPPED-ADDRESSからAddressのデータを取得する。

コード:

#include <cstdio>
#include <string>
#include <winsock2.h>

std::string get_global_ip(void) {
	// サーバー情報
	static const char* server_addr="stun.l.google.com";
	static const int server_port=19302;
	static const unsigned int timeout=2000; // ms
	// ソケット関連の変数
	SOCKET sock;
	sockaddr_in addr;
	unsigned int host_buf;
	unsigned int* host_ptr_buf[2]={&host_buf,NULL};
	unsigned int** host=host_ptr_buf;
	/* STUN Message Type = 0x0001 (Binding Request)
	 * Message Length = 0x0000
	 * Transaction ID = (All 0x00)
	 */
	unsigned char send_buf[20]={0x00,0x01};
	unsigned char recv_buf[70000];
	// UDPのソケットを作成
	sock=socket(AF_INET,SOCK_DGRAM,0);
	if(sock==INVALID_SOCKET)throw std::string("socket creation failed");
	// タイムアウトを設定
	if(setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout))==SOCKET_ERROR) {
		closesocket(sock);
		throw std::string("setsockopt failed");
	}
	// 接続先の情報を設定
	addr.sin_family=AF_INET;
	addr.sin_port=htons(server_port);
	host_buf=inet_addr(server_addr);
	if(host_buf==0xffffffff) {
		hostent* host_info;
		host_info=gethostbyname(server_addr);
		if(host_info==NULL) {
			closesocket(sock);
			throw std::string("gethostbyname failed");
		}
		host=(unsigned int**)host_info->h_addr_list;
	}
	while(*host!=NULL) {
		int recv_len;
		sockaddr src_addr;
		int addrlen=sizeof(src_addr);
		// アドレスを設定
		addr.sin_addr.S_un.S_addr=**(host++);
		// メッセージを送信
		if(sendto(sock,(const char*)send_buf,sizeof(send_buf),0,(struct sockaddr*)&addr,sizeof(addr))==sizeof(send_buf)) {
			// メッセージを受信
			if((recv_len=recvfrom(sock,(char*)recv_buf,sizeof(recv_buf),0,&src_addr,&addrlen))>=32) {
				// それっぽい長さのメッセージ
				if(recv_buf[0]==0x01 && recv_buf[1]==0x01) {
					// Binding Response
					int length=(recv_buf[2]<<8)|recv_buf[3];
					if(recv_len<20+length) {
						closesocket(sock);
						throw std::string("bad Message Length");
					}
					unsigned char* now_message=&recv_buf[20];
					while(length>0) {
						int now_id=(now_message[0]<<8)|now_message[1];
						int now_length=(now_message[2]<<8)|now_message[3];
						if(now_length+4>length) {
							closesocket(sock);
							throw std::string("bad Length");
						}
						if(now_id==0x0001) {
							char buffer[1024];
							// MAPPED-ADDRESS
							if(now_message[5]!=0x01) {
								closesocket(sock);
								throw std::string("unsupported address family");
							}
							sprintf(buffer,"%d.%d.%d.%d",
								now_message[8],now_message[9],now_message[10],now_message[11]);
							closesocket(sock);
							return std::string(buffer);
						}
						now_message+=now_length+4;
						length-=now_length+4;
						if(length>0 && length<4) {
							closesocket(sock);
							throw std::string("bad Message Length (Attribute too short)");
						}
					}
					closesocket(sock);
					throw std::string("MAPPED-ADDRESS not found");
				} else if(recv_buf[0]==0x01 && recv_buf[1]==0x11) {
					// Binding Error Response
					closesocket(sock);
					throw std::string("received Binding Error Response");
				} else {
					closesocket(sock);
					throw std::string("received unknown response");
				}
			}
		}
	}
	closesocket(sock);
	throw std::string("no response fron server");
}

int main(void) {
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,0),&wsaData);
	try {
		std::string addr=get_global_ip();
		printf("addr = %s\n",addr.c_str());
	} catch(std::string e) {
		printf("ERROR: %s\n",e.c_str());
	}
	WSACleanup();
}
コードは利用・改造自由です。

参考文献(サイト)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

返信

“サンプルを共有するコミュニティ” へ戻る