PHPを実行してUDPの53ポートでソケット待ち受け開始。
PHP実行中のマシンをDNSサーバに設定して、PINGとかをどこかに送ってみる。
初めてUDPやったけど、やっぱりUDPはTCPより簡単だった。
リクエストを受け取った瞬間に接続が絶たれるから同時接続云々考えなくてもいいみたい。
もっとリクエスト数が多かったら違うのかも?
とりあえず、今回はリクエストをパースするところまで。
PHPソース
<?php // init set_time_limit(0); class SocketServerUDP{ // server settings public $address = '0.0.0.0'; public $port = 53; public $read_length = 512; // resource private $_sock; public function __construct() { } public function create() { // create if (($this->_sock = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) { throw new Exception("socket_create() failed: ".socket_strerror($this->_sock)); } // bind if (@socket_bind($this->_sock, $this->address, $this->port) === false) { throw new Exception("socket_bind() failed: ".socket_strerror(socket_last_error($this->_sock))); } return true; } public function run() { while (true) { // recvfrom $from = ''; $port = 0; if (($msgsock = socket_recvfrom($this->_sock, $buf, $this->read_length, 0, $from, $port)) === false) { echo "socket_recvfrom() failed: ".socket_strerror(socket_last_error($this->_sock)); break; } $filename = dirname(__FILE__).'/dump/'.date('Ymd-His-').preg_replace('/ .+/', '', microtime()); file_put_contents($filename, $buf); echo "FROM : ".$from."\n"; echo "PORT : ".$port."\n"; $dns = new DNS(); print_r($dns->parseRequest($buf)); } } } class DNS { private $_data; private $_type = array( '1' => 'A', '2' => 'NS', '5' => 'CNAME', '6' => 'SOA', '12' => 'PTR', '15' => 'MX' ); private $_class = array( '1' => 'IN' ); public function parseRequest($request) { $head = unpack('n*', substr($request, 0, 12)); $body = substr($request, 12); // result $data = array( 'head' => array( 'id' => $head[1], 'flag' => array( 'qr' => ($head[2] & 32768)>>15, 'opcode' => ($head[2] & 30720)>>11, 'aa' => ($head[2] & 1024)>>10, 'tc' => ($head[2] & 512)>>9, 'rd' => ($head[2] & 256)>>8, 'ra' => ($head[2] & 128)>>7, 'z' => ($head[2] & 112)>>4, 'rcode' => ($head[2] & 15) ), 'qdcount' => $head[3], 'ancount' => $head[4], 'nscount' => $head[5], 'arcount' => $head[6] ), 'question' => array() ); $body_arr = array_values(unpack('C*', $body)); $i = 0; for ($j=0; $j<$data['head']['qdcount']; $j++) { $question = array( 'qname' => '', 'qtype' => 0, 'qclass' => 0 ); while (true) { if (!isset($body_arr[$i]) || $body_arr[$i]==0) { break; } list(,$len) = unpack('h', $body_arr[$i]); $question['qname'] .= substr($body, $i+1, $len).'.'; $i += $len + 1; } list(,$question['qtype']) = unpack('v', substr($body, $i, 2)); $i += 2; list(,$question['qclass']) = unpack('v', substr($body, $i, 2)); $i += 2; $data['question'][] = $question; } $this->_data = $data; return $data; } } $ss = new SocketServerUDP(); try { $ss->create(); $ss->run(); } catch(Exception $e) { echo $e; }
正引きリクエストをパース
FROM : 127.0.0.1 PORT : 32786 Array ( [head] => Array ( [id] => 37150 [flag] => Array ( [qr] => 0 [opcode] => 0 [aa] => 0 [tc] => 0 [rd] => 1 [ra] => 0 [z] => 0 [rcode] => 0 ) [qdcount] => 1 [ancount] => 0 [nscount] => 0 [arcount] => 0 ) [question] => Array ( [0] => Array ( [qname] => google.co.jp. [qtype] => 0 [qclass] => 1 ) ) )
逆引きリクエストをパース
FROM : 127.0.0.1 PORT : 32786 Array ( [head] => Array ( [id] => 51892 [flag] => Array ( [qr] => 0 [opcode] => 0 [aa] => 0 [tc] => 0 [rd] => 1 [ra] => 0 [z] => 0 [rcode] => 0 ) [qdcount] => 1 [ancount] => 0 [nscount] => 0 [arcount] => 0 ) [question] => Array ( [0] => Array ( [qname] => 50.0.168.192.in-addr.arpa. [qtype] => 0 [qclass] => 12 ) ) )
参考URL
3 Minutes Networking No.66
http://www5e.biglobe.ne.jp/~aji/3min/66.html
DNSの例 (実際のパケットダンプを使った説明)
http://www.db.is.kyushu-u.ac.jp/rinkou/unixnet/f-88.files/frame.htm
名前をIPアドレスに変換する---DNS・その2(第54回):TCP/IP再入門
http://pc.nikkeibp.co.jp/article/knowhow/20080825/1007323/?P=2
flashのsocketでDNSにリクエスト(1) - あすのかぜ
http://d.hatena.ne.jp/ASnoKaze/20091111/1257998451
RFC1035 ドメイン名−実装と仕様書
http://www5d.biglobe.ne.jp/~stssk/rfc/rfc1035j.html